Kavita+ Tweaks (#2595)

This commit is contained in:
Joe Milazzo 2024-01-09 16:04:25 -06:00 committed by GitHub
parent e21144bf6b
commit 3dcf7750f7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 501 additions and 414 deletions

View File

@ -1,6 +1,6 @@
name: Bug Report
description: Create a report to help us improve
title: ""
title: "Bug Title Here" # Add a title field
labels: ["needs-triage"]
assignees:
body:
@ -16,7 +16,7 @@ body:
id: what-happened
attributes:
label: What happened?
description: Also tell us, what steps you took so we can try to reproduce.
description: Also tell us what steps you took so we can try to reproduce.
placeholder: Tell us what you see!
value: ""
validations:
@ -52,7 +52,7 @@ body:
- type: dropdown
id: desktop-OS
attributes:
label: If issue being seen on Desktop, what OS are you running where you see the issue?
label: If the issue is being seen on Desktop, what OS are you running where you see the issue?
multiple: false
options:
- Windows
@ -61,7 +61,7 @@ body:
- type: dropdown
id: desktop-browsers
attributes:
label: If issue being seen in the UI, what browsers are you seeing the problem on?
label: If the issue is being seen in the UI, what browsers are you seeing the problem on?
multiple: true
options:
- Firefox
@ -71,7 +71,7 @@ body:
- type: dropdown
id: mobile-OS
attributes:
label: If issue being seen on Mobile, what OS are you running where you see the issue?
label: If the issue is being seen on Mobile, what OS are you running where you see the issue?
multiple: false
options:
- Android
@ -79,7 +79,7 @@ body:
- type: dropdown
id: mobile-browsers
attributes:
label: If issue being seen on UI, what browsers are you seeing the problem on?
label: If the issue is being seen on the UI, what browsers are you seeing the problem on?
multiple: true
options:
- Firefox
@ -97,7 +97,7 @@ body:
attributes:
label: Additional Notes
description: Any other information about the issue not covered in this form?
placeholder: e.g. Running Kavita on a raspberry pi, updating from X version, using LSIO container, etc
placeholder: e.g. Running Kavita on a Raspberry Pi, updating from X version, using LSIO container, etc
value: ""
validations:
required: true

View File

@ -10,8 +10,8 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="BenchmarkDotNet" Version="0.13.11" />
<PackageReference Include="BenchmarkDotNet.Annotations" Version="0.13.11" />
<PackageReference Include="BenchmarkDotNet" Version="0.13.12" />
<PackageReference Include="BenchmarkDotNet.Annotations" Version="0.13.12" />
<PackageReference Include="NSubstitute" Version="5.1.0" />
</ItemGroup>

View File

@ -6,13 +6,13 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="8.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="8.0.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
<PackageReference Include="NSubstitute" Version="5.1.0" />
<PackageReference Include="System.IO.Abstractions.TestingHelpers" Version="20.0.4" />
<PackageReference Include="TestableIO.System.IO.Abstractions.Wrappers" Version="20.0.4" />
<PackageReference Include="xunit" Version="2.6.3" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.5">
<PackageReference Include="xunit" Version="2.6.5" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.6">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>

View File

@ -292,6 +292,7 @@ public class MangaParserTests
[InlineData("Accel World Chapter 001 Volume 002", "1")]
[InlineData("Bleach 001-003", "1-3")]
[InlineData("Accel World Volume 2", "0")]
[InlineData("Historys Strongest Disciple Kenichi_v11_c90-98", "90-98")]
public void ParseChaptersTest(string filename, string expected)
{
Assert.Equal(expected, API.Services.Tasks.Scanner.Parser.Parser.ParseChapter(filename));

View File

@ -53,7 +53,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.0">
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.1">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
@ -63,26 +63,26 @@
<PackageReference Include="ExCSS" Version="4.2.4" />
<PackageReference Include="Flurl" Version="3.0.7" />
<PackageReference Include="Flurl.Http" Version="3.2.4" />
<PackageReference Include="Hangfire" Version="1.8.6" />
<PackageReference Include="Hangfire" Version="1.8.7" />
<PackageReference Include="Hangfire.InMemory" Version="0.6.0" />
<PackageReference Include="Hangfire.MaximumConcurrentExecutions" Version="1.1.0" />
<PackageReference Include="Hangfire.MemoryStorage.Core" Version="1.4.0" />
<PackageReference Include="Hangfire.Storage.SQLite" Version="0.3.4" />
<PackageReference Include="HtmlAgilityPack" Version="1.11.54" />
<PackageReference Include="Hangfire.Storage.SQLite" Version="0.4.0" />
<PackageReference Include="HtmlAgilityPack" Version="1.11.57" />
<PackageReference Include="MarkdownDeep.NET.Core" Version="1.5.0.4" />
<PackageReference Include="Hangfire.AspNetCore" Version="1.8.6" />
<PackageReference Include="Hangfire.AspNetCore" Version="1.8.7" />
<PackageReference Include="Microsoft.AspNetCore.SignalR" Version="1.1.0" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="8.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="8.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.1" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="8.0.1" />
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="8.0.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.1" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
<PackageReference Include="Microsoft.IO.RecyclableMemoryStream" Version="3.0.0" />
<PackageReference Include="MimeTypeMapOfficial" Version="1.0.17" />
<PackageReference Include="Nager.ArticleNumber" Version="1.0.7" />
<PackageReference Include="NetVips" Version="2.4.0" />
<PackageReference Include="NetVips.Native" Version="8.15.0" />
<PackageReference Include="NReco.Logging.File" Version="1.1.7" />
<PackageReference Include="NReco.Logging.File" Version="1.2.0" />
<PackageReference Include="Serilog" Version="3.1.1" />
<PackageReference Include="Serilog.AspNetCore" Version="8.0.0" />
<PackageReference Include="Serilog.Enrichers.Thread" Version="3.2.0-dev-00752" />
@ -92,17 +92,17 @@
<PackageReference Include="Serilog.Sinks.Console" Version="5.0.1" />
<PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />
<PackageReference Include="Serilog.Sinks.SignalR.Core" Version="0.1.2" />
<PackageReference Include="SharpCompress" Version="0.34.2" />
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.1" />
<PackageReference Include="SharpCompress" Version="0.35.0" />
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.2" />
<PackageReference Include="SonarAnalyzer.CSharp" Version="9.15.0.81779">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
<PackageReference Include="Swashbuckle.AspNetCore.Filters" Version="7.0.12" />
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="7.0.3" />
<PackageReference Include="Swashbuckle.AspNetCore.Filters" Version="8.0.0" />
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="7.1.2" />
<PackageReference Include="System.IO.Abstractions" Version="20.0.4" />
<PackageReference Include="System.Drawing.Common" Version="8.0.0" />
<PackageReference Include="System.Drawing.Common" Version="8.0.1" />
<PackageReference Include="VersOne.Epub" Version="3.3.1" />
</ItemGroup>

View File

@ -39,22 +39,27 @@ public class ScrobblingController : BaseApiController
_localizationService = localizationService;
}
/// <summary>
/// Get the current user's AniList token
/// </summary>
/// <returns></returns>
[HttpGet("anilist-token")]
public async Task<ActionResult> GetAniListToken()
{
// Validate the license
var user = await _unitOfWork.UserRepository.GetUserByUsernameAsync(User.GetUsername());
if (user == null) return Unauthorized();
return Ok(user.AniListAccessToken);
}
/// <summary>
/// Update the current user's AniList token
/// </summary>
/// <param name="dto"></param>
/// <returns></returns>
[HttpPost("update-anilist-token")]
public async Task<ActionResult> UpdateAniListToken(AniListUpdateDto dto)
{
// Validate the license
var user = await _unitOfWork.UserRepository.GetUserByUsernameAsync(User.GetUsername());
if (user == null) return Unauthorized();
@ -71,6 +76,11 @@ public class ScrobblingController : BaseApiController
return Ok();
}
/// <summary>
/// Checks if the current Scrobbling token for the given Provider has expired for the current user
/// </summary>
/// <param name="provider"></param>
/// <returns></returns>
[HttpGet("token-expired")]
public async Task<ActionResult<bool>> HasTokenExpired(ScrobbleProvider provider)
{
@ -159,15 +169,20 @@ public class ScrobblingController : BaseApiController
var user = await _unitOfWork.UserRepository.GetUserByIdAsync(User.GetUserId(), AppUserIncludes.ScrobbleHolds);
if (user == null) return Unauthorized();
if (user.ScrobbleHolds.Any(s => s.SeriesId == seriesId))
return Ok(await _localizationService.Translate(User.GetUserId(), "nothing-to-do"));
return Ok(await _localizationService.Translate(user.Id, "nothing-to-do"));
var seriesHold = new ScrobbleHoldBuilder().WithSeriesId(seriesId).Build();
var seriesHold = new ScrobbleHoldBuilder()
.WithSeriesId(seriesId)
.Build();
user.ScrobbleHolds.Add(seriesHold);
_unitOfWork.UserRepository.Update(user);
try
{
_unitOfWork.UserRepository.Update(user);
await _unitOfWork.CommitAsync();
// When a hold is placed on a series, clear any pre-existing Scrobble Events
await _scrobblingService.ClearEventsForSeries(user.Id, seriesId);
return Ok();
}
catch (DbUpdateConcurrencyException ex)

View File

@ -27,7 +27,7 @@ public interface IScrobbleRepository
Task ClearScrobbleErrors();
Task<bool> HasErrorForSeries(int seriesId);
Task<ScrobbleEvent?> GetEvent(int userId, int seriesId, ScrobbleEventType eventType);
Task<IEnumerable<ScrobbleEventDto>> GetUserEvents(int userId);
Task<IEnumerable<ScrobbleEvent>> GetUserEventsForSeries(int userId, int seriesId);
Task<PagedList<ScrobbleEventDto>> GetUserEvents(int userId, ScrobbleEventFilter filter, UserParams pagination);
}
@ -127,16 +127,17 @@ public class ScrobbleRepository : IScrobbleRepository
return await _context.ScrobbleEvent.FirstOrDefaultAsync(e =>
e.AppUserId == userId && e.SeriesId == seriesId && e.ScrobbleEventType == eventType);
}
public async Task<IEnumerable<ScrobbleEventDto>> GetUserEvents(int userId)
public async Task<IEnumerable<ScrobbleEvent>> GetUserEventsForSeries(int userId, int seriesId)
{
return await _context.ScrobbleEvent
.Where(e => e.AppUserId == userId)
.Where(e => e.AppUserId == userId && !e.IsProcessed)
.Include(e => e.Series)
.OrderBy(e => e.LastModifiedUtc)
.AsSplitQuery()
.ProjectTo<ScrobbleEventDto>(_mapper.ConfigurationProvider)
.ToListAsync();
}
public async Task<PagedList<ScrobbleEventDto>> GetUserEvents(int userId, ScrobbleEventFilter filter, UserParams pagination)
{
var query = _context.ScrobbleEvent
@ -146,6 +147,7 @@ public class ScrobbleRepository : IScrobbleRepository
.WhereIf(!string.IsNullOrEmpty(filter.Query), s =>
EF.Functions.Like(s.Series.Name, $"%{filter.Query}%")
)
.WhereIf(!filter.IncludeReviews, e => e.ScrobbleEventType != ScrobbleEventType.Review)
.AsSplitQuery()
.ProjectTo<ScrobbleEventDto>(_mapper.ConfigurationProvider);

View File

@ -15,4 +15,8 @@ public class ScrobbleEventFilter
/// A query to search against
/// </summary>
public string Query { get; set; }
/// <summary>
/// Include reviews in the result - Note: Review Scrobbling is disabled
/// </summary>
public bool IncludeReviews { get; set; } = false;
}

View File

@ -53,6 +53,7 @@ public interface IScrobblingService
[AutomaticRetry(Attempts = 3, OnAttemptsExceeded = AttemptsExceededAction.Delete)]
Task ProcessUpdatesSinceLastSync();
Task CreateEventsFromExistingHistory(int userId = 0);
Task ClearEventsForSeries(int userId, int seriesId);
}
public class ScrobblingService : IScrobblingService
@ -542,6 +543,26 @@ public class ScrobblingService : IScrobblingService
}
}
/// <summary>
/// Removes all events (active) that are tied to a now-on hold series
/// </summary>
/// <param name="userId"></param>
/// <param name="seriesId"></param>
public async Task ClearEventsForSeries(int userId, int seriesId)
{
_logger.LogInformation("Clearing Pre-existing Scrobble events for Series {SeriesId} by User {UserId} as Series is now on hold list", seriesId, userId);
var events = await _unitOfWork.ScrobbleRepository.GetUserEventsForSeries(userId, seriesId);
foreach (var scrobble in events)
{
_unitOfWork.ScrobbleRepository.Remove(scrobble);
}
await _unitOfWork.CommitAsync();
}
/// <summary>
/// Removes all events that have been processed that are 7 days old
/// </summary>
[DisableConcurrentExecution(60 * 60 * 60)]
[AutomaticRetry(Attempts = 3, OnAttemptsExceeded = AttemptsExceededAction.Delete)]
public async Task ClearProcessedEvents()
@ -594,10 +615,10 @@ public class ScrobblingService : IScrobblingService
.Where(e => librariesWithScrobbling.Contains(e.LibraryId))
.Where(e => !errors.Contains(e.SeriesId))
.ToList();
var reviewEvents = (await _unitOfWork.ScrobbleRepository.GetByEvent(ScrobbleEventType.Review))
.Where(e => librariesWithScrobbling.Contains(e.LibraryId))
.Where(e => !errors.Contains(e.SeriesId))
.ToList();
// var reviewEvents = (await _unitOfWork.ScrobbleRepository.GetByEvent(ScrobbleEventType.Review))
// .Where(e => librariesWithScrobbling.Contains(e.LibraryId))
// .Where(e => !errors.Contains(e.SeriesId))
// .ToList();
var decisions = addToWantToRead
.GroupBy(item => new { item.SeriesId, item.AppUserId })
.Select(group => new
@ -624,7 +645,7 @@ public class ScrobblingService : IScrobblingService
await SetAndCheckRateLimit(userRateLimits, user, license.Value);
}
var totalProgress = readEvents.Count + decisions.Count + ratingEvents.Count + decisions.Count + reviewEvents.Count;
var totalProgress = readEvents.Count + decisions.Count + ratingEvents.Count + decisions.Count;// + reviewEvents.Count;
_logger.LogInformation("Found {TotalEvents} Scrobble Events", totalProgress);
try
@ -671,21 +692,21 @@ public class ScrobblingService : IScrobblingService
Year = evt.Series.Metadata.ReleaseYear
}));
progressCounter = await ProcessEvents(reviewEvents, userRateLimits, usersToScrobble.Count, progressCounter,
totalProgress, evt => Task.FromResult(new ScrobbleDto()
{
Format = evt.Format,
AniListId = evt.AniListId,
MALId = (int?) evt.MalId,
ScrobbleEventType = evt.ScrobbleEventType,
AniListToken = evt.AppUser.AniListAccessToken,
SeriesName = evt.Series.Name,
LocalizedSeriesName = evt.Series.LocalizedName,
Rating = evt.Rating,
Year = evt.Series.Metadata.ReleaseYear,
ReviewBody = evt.ReviewBody,
ReviewTitle = evt.ReviewTitle
}));
// progressCounter = await ProcessEvents(reviewEvents, userRateLimits, usersToScrobble.Count, progressCounter,
// totalProgress, evt => Task.FromResult(new ScrobbleDto()
// {
// Format = evt.Format,
// AniListId = evt.AniListId,
// MALId = (int?) evt.MalId,
// ScrobbleEventType = evt.ScrobbleEventType,
// AniListToken = evt.AppUser.AniListAccessToken,
// SeriesName = evt.Series.Name,
// LocalizedSeriesName = evt.Series.LocalizedName,
// Rating = evt.Rating,
// Year = evt.Series.Metadata.ReleaseYear,
// ReviewBody = evt.ReviewBody,
// ReviewTitle = evt.ReviewTitle
// }));
progressCounter = await ProcessEvents(decisions, userRateLimits, usersToScrobble.Count, progressCounter,
totalProgress, evt => Task.FromResult(new ScrobbleDto()

View File

@ -49,6 +49,7 @@ public class DefaultParser : IDefaultParser
// If library type is Image or this is not a cover image in a non-image library, then use dedicated parsing mechanism
if (type == LibraryType.Image || Parser.IsImage(filePath))
{
// TODO: We can move this up one level
return ParseImage(filePath, rootPath, ret);
}
@ -78,7 +79,7 @@ public class DefaultParser : IDefaultParser
var edition = Parser.ParseEdition(fileName);
if (!string.IsNullOrEmpty(edition))
{
ret.Series = Parser.CleanTitle(ret.Series.Replace(edition, ""), type is LibraryType.Comic);
ret.Series = Parser.CleanTitle(ret.Series.Replace(edition, string.Empty), type is LibraryType.Comic);
ret.Edition = edition;
}

View File

@ -543,7 +543,7 @@ public static class Parser
{
// Historys Strongest Disciple Kenichi_v11_c90-98.zip, ...c90.5-100.5
new Regex(
@"(\b|_)(c|ch)(\.?\s?)(?<Chapter>(\d+(\.\d)?)-?(\d+(\.\d)?)?)",
@"(\b|_)(c|ch)(\.?\s?)(?<Chapter>(\d+(\.\d)?)(-\d+(\.\d)?)?)",
MatchOptions, RegexTimeout),
// [Suihei Kiki]_Kasumi_Otoko_no_Ko_[Taruby]_v1.1.zip
new Regex(

View File

@ -14,7 +14,7 @@
<PackageReference Include="Flurl.Http" Version="3.2.4" />
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
<PackageReference Include="SonarAnalyzer.CSharp" Version="9.15.0.81779">
<PackageReference Include="SonarAnalyzer.CSharp" Version="9.16.0.82469">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>

636
UI/Web/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -13,17 +13,17 @@
},
"private": true,
"dependencies": {
"@angular/animations": "^17.0.6",
"@angular/animations": "^17.0.8",
"@angular/cdk": "^17.0.4",
"@angular/common": "^17.0.6",
"@angular/compiler": "^17.0.6",
"@angular/core": "^17.0.6",
"@angular/forms": "^17.0.6",
"@angular/localize": "^17.0.6",
"@angular/platform-browser": "^17.0.6",
"@angular/platform-browser-dynamic": "^17.0.6",
"@angular/router": "^17.0.6",
"@fortawesome/fontawesome-free": "^6.4.2",
"@angular/common": "^17.0.8",
"@angular/compiler": "^17.0.8",
"@angular/core": "^17.0.8",
"@angular/forms": "^17.0.8",
"@angular/localize": "^17.0.8",
"@angular/platform-browser": "^17.0.8",
"@angular/platform-browser-dynamic": "^17.0.8",
"@angular/router": "^17.0.8",
"@fortawesome/fontawesome-free": "^6.5.1",
"@iharbeck/ngx-virtual-scroller": "^17.0.0",
"@iplab/ngx-file-upload": "^17.0.0",
"@microsoft/signalr": "^7.0.12",
@ -42,7 +42,7 @@
"luxon": "^3.4.4",
"ng-circle-progress": "^1.7.1",
"ng-lazyload-image": "^9.1.3",
"ng-select2-component": "^13.0.9",
"ng-select2-component": "^14.0.0",
"ngx-color-picker": "^16.0.0",
"ngx-extended-pdf-viewer": "^18.1.9",
"ngx-file-drop": "^16.0.0",
@ -58,17 +58,17 @@
"zone.js": "^0.14.2"
},
"devDependencies": {
"@angular-devkit/build-angular": "^17.0.7",
"@angular-eslint/builder": "^17.1.1",
"@angular-eslint/eslint-plugin": "^17.1.1",
"@angular-eslint/eslint-plugin-template": "^17.1.1",
"@angular-eslint/schematics": "^17.1.1",
"@angular-eslint/template-parser": "^17.1.1",
"@angular/cli": "^17.0.7",
"@angular/compiler-cli": "^17.0.6",
"@angular-devkit/build-angular": "^17.0.9",
"@angular-eslint/builder": "^17.2.0",
"@angular-eslint/eslint-plugin": "^17.2.0",
"@angular-eslint/eslint-plugin-template": "^17.2.0",
"@angular-eslint/schematics": "^17.2.0",
"@angular-eslint/template-parser": "^17.2.0",
"@angular/cli": "^17.0.9",
"@angular/compiler-cli": "^17.0.8",
"@types/d3": "^7.4.3",
"@types/file-saver": "^2.0.7",
"@types/luxon": "^3.3.7",
"@types/luxon": "^3.4.0",
"@types/node": "^20.10.0",
"@typescript-eslint/eslint-plugin": "^6.13.0",
"@typescript-eslint/parser": "^6.13.0",

View File

@ -6,6 +6,8 @@ import {environment} from "../../environments/environment";
import {SideNavStream} from "../_models/sidenav/sidenav-stream";
import {TextResonse} from "../_types/text-response";
import {DashboardStream} from "../_models/dashboard/dashboard-stream";
import {AccountService} from "./account.service";
import {tap} from "rxjs/operators";
@Injectable({
providedIn: 'root'
@ -34,9 +36,16 @@ export class NavService {
private renderer: Renderer2;
baseUrl = environment.apiUrl;
constructor(@Inject(DOCUMENT) private document: Document, rendererFactory: RendererFactory2, private httpClient: HttpClient) {
constructor(@Inject(DOCUMENT) private document: Document, rendererFactory: RendererFactory2, private httpClient: HttpClient, private accountService: AccountService) {
this.renderer = rendererFactory.createRenderer(null, null);
this.showNavBar();
// To avoid flashing, let's check if we are authenticated before we show
this.accountService.currentUser$.subscribe(u => {
if (u) {
this.showNavBar();
}
});
const sideNavState = (localStorage.getItem(this.localStorageSideNavKey) === 'true') || false;
this.sideNavCollapseSource.next(sideNavState);
this.showSideNav();

View File

@ -84,12 +84,19 @@ export class BookLineOverlayComponent implements OnInit {
const selection = window.getSelection();
if (!event.target) return;
if ((selection === null || selection === undefined || selection.toString().trim() === '' || selection.toString().trim() === this.selectedText)) {
if (this.selectedText !== '') {
event.preventDefault();
event.stopPropagation();
}
this.reset();
const isRightClick = (event instanceof MouseEvent && event.button === 2);
if (!isRightClick) {
this.reset();
}
return;
}

View File

@ -1,4 +1,4 @@
import { ChangeDetectionStrategy, Component } from '@angular/core';
import {ChangeDetectionStrategy, Component, inject} from '@angular/core';
import { Validators, FormGroup, FormControl, ReactiveFormsModule } from '@angular/forms';
import { Router } from '@angular/router';
import { ToastrService } from 'ngx-toastr';
@ -6,6 +6,7 @@ import { AccountService } from 'src/app/_services/account.service';
import { NgIf } from '@angular/common';
import { SplashContainerComponent } from '../splash-container/splash-container.component';
import {TranslocoDirective} from "@ngneat/transloco";
import {NavService} from "../../../_services/nav.service";
@Component({
selector: 'app-reset-password',
@ -17,12 +18,19 @@ import {TranslocoDirective} from "@ngneat/transloco";
})
export class ResetPasswordComponent {
private readonly router = inject(Router);
private readonly accountService = inject(AccountService);
private readonly toastr = inject(ToastrService);
private readonly navService = inject(NavService);
registerForm: FormGroup = new FormGroup({
email: new FormControl('', [Validators.required, Validators.email]),
});
constructor(private router: Router, private accountService: AccountService,
private toastr: ToastrService) {}
constructor() {
this.navService.hideNavBar();
this.navService.hideSideNav();
}
submit() {
const model = this.registerForm.get('email')?.value;

View File

@ -1,4 +1,4 @@
<div class="mx-auto login">
<div class="mx-auto login" [ngStyle]="{'height': (navService.navbarVisible$ | async) ? 'calc(var(--vh, 1vh) * 100 - 57px)' : 'calc(var(--vh, 1vh) * 100)'}">
<div class="row row-cols-4 row-cols-md-4 row-cols-sm-2 row-cols-xs-2">
<div class="col align-self-center card p-3">

View File

@ -1,10 +1,18 @@
import { ChangeDetectionStrategy, Component } from '@angular/core';
import {ChangeDetectionStrategy, Component, inject} from '@angular/core';
import {AsyncPipe, NgStyle} from "@angular/common";
import {NavService} from "../../../_services/nav.service";
@Component({
selector: 'app-splash-container',
templateUrl: './splash-container.component.html',
styleUrls: ['./splash-container.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
standalone: true
selector: 'app-splash-container',
templateUrl: './splash-container.component.html',
styleUrls: ['./splash-container.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [
NgStyle,
AsyncPipe
],
standalone: true
})
export class SplashContainerComponent {}
export class SplashContainerComponent {
protected readonly navService = inject(NavService);
}

View File

@ -26,6 +26,4 @@ export class UserHoldsComponent {
private readonly scrobblingService = inject(ScrobblingService);
private readonly destroyRef = inject(DestroyRef);
holds$ = this.scrobblingService.getHolds().pipe(takeUntilDestroyed(this.destroyRef), shareReplay());
constructor() {}
}

View File

@ -7335,6 +7335,7 @@
"tags": [
"Scrobbling"
],
"summary": "Get the current user's AniList token",
"responses": {
"200": {
"description": "Success"
@ -7347,7 +7348,9 @@
"tags": [
"Scrobbling"
],
"summary": "Update the current user's AniList token",
"requestBody": {
"description": "",
"content": {
"application/json": {
"schema": {
@ -7378,10 +7381,12 @@
"tags": [
"Scrobbling"
],
"summary": "Checks if the current Scrobbling token for the given Provider has expired for the current user",
"parameters": [
{
"name": "provider",
"in": "query",
"description": "",
"schema": {
"enum": [
0,
@ -17178,6 +17183,10 @@
"type": "string",
"description": "A query to search against",
"nullable": true
},
"includeReviews": {
"type": "boolean",
"description": "Include reviews in the result - Note: Review Scrobbling is disabled"
}
},
"additionalProperties": false