Release Testing Day 2 (#1493)

* Added a no data section to collection detail.

* Remove an optimization for skipping the whole library scan as it wasn't reliable

* When resetting password, ensure the input is colored correctly

* Fixed setting new password after resetting, throwing an error despite it actually being successful.

Fixed incorrect messaging for Password Reset page.

* Fixed a bug where reset password would show the side nav button and skew the page.

Updated a lot of references to use Typed version for formcontrols.

* Removed a migration from 0.5.0, 6 releases ago.

* Added a null check so we don't throw an exception when connecting with signalR on unauthenticated users.
This commit is contained in:
Joseph Milazzo 2022-08-31 09:50:24 -05:00 committed by GitHub
parent 8e21a7091f
commit 2cd94e7db4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
32 changed files with 155 additions and 254 deletions

View File

@ -579,17 +579,26 @@ namespace API.Controllers
[HttpPost("confirm-password-reset")] [HttpPost("confirm-password-reset")]
public async Task<ActionResult<string>> ConfirmForgotPassword(ConfirmPasswordResetDto dto) public async Task<ActionResult<string>> ConfirmForgotPassword(ConfirmPasswordResetDto dto)
{ {
var user = await _unitOfWork.UserRepository.GetUserByEmailAsync(dto.Email); try
if (user == null)
{ {
return BadRequest("Invalid Details"); var user = await _unitOfWork.UserRepository.GetUserByEmailAsync(dto.Email);
if (user == null)
{
return BadRequest("Invalid Details");
}
var result = await _userManager.VerifyUserTokenAsync(user, TokenOptions.DefaultProvider,
"ResetPassword", dto.Token);
if (!result) return BadRequest("Unable to reset password, your email token is not correct.");
var errors = await _accountService.ChangeUserPassword(user, dto.Password);
return errors.Any() ? BadRequest(errors) : Ok("Password updated");
}
catch (Exception ex)
{
_logger.LogError(ex, "There was an unexpected error when confirming new password");
return BadRequest("There was an unexpected error when confirming new password");
} }
var result = await _userManager.VerifyUserTokenAsync(user, TokenOptions.DefaultProvider, "ResetPassword", dto.Token);
if (!result) return BadRequest("Unable to reset password, your email token is not correct.");
var errors = await _accountService.ChangeUserPassword(user, dto.Password);
return errors.Any() ? BadRequest(errors) : Ok("Password updated");
} }

View File

@ -1,105 +0,0 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using API.Comparators;
using API.Entities.Enums;
using API.Services;
using Microsoft.Extensions.Logging;
namespace API.Data;
/// <summary>
/// Responsible to migrate existing bookmarks to files. Introduced in v0.4.9.27
/// </summary>
public static class MigrateBookmarks
{
/// <summary>
/// This will migrate existing bookmarks to bookmark folder based.
/// If the bookmarks folder already exists, this will not run.
/// </summary>
/// <remarks>Bookmark directory is configurable. This will always use the default bookmark directory.</remarks>
/// <param name="directoryService"></param>
/// <param name="unitOfWork"></param>
/// <param name="logger"></param>
/// <param name="cacheService"></param>
/// <returns></returns>
public static async Task Migrate(IDirectoryService directoryService, IUnitOfWork unitOfWork,
ILogger<Program> logger, ICacheService cacheService)
{
var bookmarkDirectory = (await unitOfWork.SettingsRepository.GetSettingAsync(ServerSettingKey.BookmarkDirectory))
.Value;
if (string.IsNullOrEmpty(bookmarkDirectory))
{
bookmarkDirectory = directoryService.BookmarkDirectory;
}
if (directoryService.Exists(bookmarkDirectory)) return;
logger.LogInformation("Bookmark migration is needed....This may take some time");
var allBookmarks = (await unitOfWork.UserRepository.GetAllBookmarksAsync()).ToList();
var uniqueChapterIds = allBookmarks.Select(b => b.ChapterId).Distinct().ToList();
var uniqueUserIds = allBookmarks.Select(b => b.AppUserId).Distinct().ToList();
foreach (var userId in uniqueUserIds)
{
foreach (var chapterId in uniqueChapterIds)
{
var chapterBookmarks = allBookmarks.Where(b => b.ChapterId == chapterId).ToList();
var chapterPages = chapterBookmarks
.Select(b => b.Page).ToList();
var seriesId = chapterBookmarks
.Select(b => b.SeriesId).First();
var mangaFiles = await unitOfWork.ChapterRepository.GetFilesForChapterAsync(chapterId);
var chapterExtractPath = directoryService.FileSystem.Path.Join(directoryService.TempDirectory, $"bookmark_c{chapterId}_u{userId}_s{seriesId}");
var numericComparer = new NumericComparer();
if (!mangaFiles.Any()) continue;
switch (mangaFiles.First().Format)
{
case MangaFormat.Image:
directoryService.ExistOrCreate(chapterExtractPath);
directoryService.CopyFilesToDirectory(mangaFiles.Select(f => f.FilePath), chapterExtractPath);
break;
case MangaFormat.Archive:
case MangaFormat.Pdf:
cacheService.ExtractChapterFiles(chapterExtractPath, mangaFiles.ToList());
break;
case MangaFormat.Epub:
continue;
default:
continue;
}
var files = directoryService.GetFilesWithExtension(chapterExtractPath, Services.Tasks.Scanner.Parser.Parser.ImageFileExtensions);
// Filter out images that aren't in bookmarks
Array.Sort(files, numericComparer);
foreach (var chapterPage in chapterPages)
{
var file = files.ElementAt(chapterPage);
var bookmark = allBookmarks.FirstOrDefault(b =>
b.ChapterId == chapterId && b.SeriesId == seriesId && b.AppUserId == userId &&
b.Page == chapterPage);
if (bookmark == null) continue;
var filename = directoryService.FileSystem.Path.GetFileName(file);
var newLocation = directoryService.FileSystem.Path.Join(
ReaderService.FormatBookmarkFolderPath(String.Empty, userId, seriesId, chapterId),
filename);
bookmark.FileName = newLocation;
directoryService.CopyFileToDirectory(file,
ReaderService.FormatBookmarkFolderPath(bookmarkDirectory, userId, seriesId, chapterId));
unitOfWork.UserRepository.Update(bookmark);
}
}
// Clear temp after each user to avoid too much space being eaten
directoryService.ClearDirectory(directoryService.TempDirectory);
}
await unitOfWork.CommitAsync();
// Run CleanupService as we cache a ton of files
directoryService.ClearDirectory(directoryService.TempDirectory);
}
}

View File

@ -406,30 +406,6 @@ public class ScannerService : IScannerService
var libraryFolderPaths = library.Folders.Select(fp => fp.Path).ToList(); var libraryFolderPaths = library.Folders.Select(fp => fp.Path).ToList();
if (!await CheckMounts(library.Name, libraryFolderPaths)) return; if (!await CheckMounts(library.Name, libraryFolderPaths)) return;
// If all library Folder paths haven't been modified since last scan, abort
// Unless the user did something on the library (delete series) and thus we can bypass this check
var wasLibraryUpdatedSinceLastScan = (library.LastModified.Truncate(TimeSpan.TicksPerMinute) >
library.LastScanned.Truncate(TimeSpan.TicksPerMinute))
&& library.LastScanned != DateTime.MinValue;
if (!forceUpdate && !wasLibraryUpdatedSinceLastScan)
{
var haveFoldersChangedSinceLastScan = library.Folders
.All(f => _directoryService.GetLastWriteTime(f.Path).Truncate(TimeSpan.TicksPerMinute) > f.LastScanned.Truncate(TimeSpan.TicksPerMinute));
// If nothing changed && library folder's have all been scanned at least once
if (!haveFoldersChangedSinceLastScan && library.Folders.All(f => f.LastScanned > DateTime.MinValue))
{
_logger.LogInformation("[ScannerService] {LibraryName} scan has no work to do. All folders have not been changed since last scan", library.Name);
await _eventHub.SendMessageAsync(MessageFactory.Info,
MessageFactory.InfoEvent($"{library.Name} scan has no work to do",
$"All folders have not been changed since last scan ({library.Folders.Max(f => f.LastScanned).ToString(CultureInfo.CurrentCulture)}). Scan will be aborted."));
BackgroundJob.Enqueue(() => _metadataService.GenerateCoversForLibrary(library.Id, false));
BackgroundJob.Enqueue(() => _wordCountAnalyzerService.ScanLibrary(library.Id, false));
return;
}
}
// Validations are done, now we can start actual scan // Validations are done, now we can start actual scan
_logger.LogInformation("[ScannerService] Beginning file scan on {LibraryName}", library.Name); _logger.LogInformation("[ScannerService] Beginning file scan on {LibraryName}", library.Name);

View File

@ -38,6 +38,7 @@ namespace API.SignalR.Presence
public async Task UserConnected(string username, string connectionId) public async Task UserConnected(string username, string connectionId)
{ {
var user = await _unitOfWork.UserRepository.GetUserByUsernameAsync(username); var user = await _unitOfWork.UserRepository.GetUserByUsernameAsync(username);
if (user == null) return;
var isAdmin = await _unitOfWork.UserRepository.IsUserAdminAsync(user); var isAdmin = await _unitOfWork.UserRepository.IsUserAdminAsync(user);
lock (OnlineUsers) lock (OnlineUsers)
{ {

View File

@ -185,8 +185,6 @@ namespace API
var themeService = serviceProvider.GetRequiredService<IThemeService>(); var themeService = serviceProvider.GetRequiredService<IThemeService>();
var dataContext = serviceProvider.GetRequiredService<DataContext>(); var dataContext = serviceProvider.GetRequiredService<DataContext>();
await MigrateBookmarks.Migrate(directoryService, unitOfWork,
logger, cacheService);
// Only run this if we are upgrading // Only run this if we are upgrading
await MigrateChangePasswordRoles.Migrate(unitOfWork, userManager); await MigrateChangePasswordRoles.Migrate(unitOfWork, userManager);

View File

@ -165,7 +165,7 @@ export class AccountService implements OnDestroy {
} }
confirmResetPasswordEmail(model: {email: string, token: string, password: string}) { confirmResetPasswordEmail(model: {email: string, token: string, password: string}) {
return this.httpClient.post(this.baseUrl + 'account/confirm-password-reset', model); return this.httpClient.post(this.baseUrl + 'account/confirm-password-reset', model, {responseType: 'json' as 'text'});
} }
resetPassword(username: string, password: string, oldPassword: string) { resetPassword(username: string, password: string, oldPassword: string) {

View File

@ -1,5 +1,5 @@
import { Component, Input, OnInit } from '@angular/core'; import { Component, Input, OnInit } from '@angular/core';
import { UntypedFormControl, UntypedFormGroup, Validators } from '@angular/forms'; import { FormControl, FormGroup, Validators } from '@angular/forms';
import { NgbActiveModal, NgbModal } from '@ng-bootstrap/ng-bootstrap'; import { NgbActiveModal, NgbModal } from '@ng-bootstrap/ng-bootstrap';
import { ToastrService } from 'ngx-toastr'; import { ToastrService } from 'ngx-toastr';
import { ConfirmService } from 'src/app/shared/confirm.service'; import { ConfirmService } from 'src/app/shared/confirm.service';
@ -17,9 +17,9 @@ export class LibraryEditorModalComponent implements OnInit {
@Input() library: Library | undefined = undefined; @Input() library: Library | undefined = undefined;
libraryForm: UntypedFormGroup = new UntypedFormGroup({ libraryForm: FormGroup = new FormGroup({
name: new UntypedFormControl('', [Validators.required]), name: new FormControl('', [Validators.required]),
type: new UntypedFormControl(0, [Validators.required]) type: new FormControl(0, [Validators.required])
}); });
selectedFolders: string[] = []; selectedFolders: string[] = [];

View File

@ -1,5 +1,5 @@
import { Component, Input, OnInit } from '@angular/core'; import { Component, Input, OnInit } from '@angular/core';
import { UntypedFormGroup, UntypedFormControl, Validators } from '@angular/forms'; import { FormGroup, FormControl, Validators } from '@angular/forms';
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
import { Library } from 'src/app/_models/library'; import { Library } from 'src/app/_models/library';
import { Member } from 'src/app/_models/member'; import { Member } from 'src/app/_models/member';
@ -19,7 +19,7 @@ export class EditUserComponent implements OnInit {
selectedLibraries: Array<number> = []; selectedLibraries: Array<number> = [];
isSaving: boolean = false; isSaving: boolean = false;
userForm: UntypedFormGroup = new UntypedFormGroup({}); userForm: FormGroup = new FormGroup({});
public get email() { return this.userForm.get('email'); } public get email() { return this.userForm.get('email'); }
public get username() { return this.userForm.get('username'); } public get username() { return this.userForm.get('username'); }
@ -28,8 +28,8 @@ export class EditUserComponent implements OnInit {
constructor(public modal: NgbActiveModal, private accountService: AccountService) { } constructor(public modal: NgbActiveModal, private accountService: AccountService) { }
ngOnInit(): void { ngOnInit(): void {
this.userForm.addControl('email', new UntypedFormControl(this.member.email, [Validators.required, Validators.email])); this.userForm.addControl('email', new FormControl(this.member.email, [Validators.required, Validators.email]));
this.userForm.addControl('username', new UntypedFormControl(this.member.username, [Validators.required])); this.userForm.addControl('username', new FormControl(this.member.username, [Validators.required]));
this.userForm.get('email')?.disable(); this.userForm.get('email')?.disable();
} }

View File

@ -1,5 +1,5 @@
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { UntypedFormControl, UntypedFormGroup, Validators } from '@angular/forms'; import { FormControl, FormGroup, Validators } from '@angular/forms';
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
import { ToastrService } from 'ngx-toastr'; import { ToastrService } from 'ngx-toastr';
import { ConfirmService } from 'src/app/shared/confirm.service'; import { ConfirmService } from 'src/app/shared/confirm.service';
@ -19,7 +19,7 @@ export class InviteUserComponent implements OnInit {
* Maintains if the backend is sending an email * Maintains if the backend is sending an email
*/ */
isSending: boolean = false; isSending: boolean = false;
inviteForm: UntypedFormGroup = new UntypedFormGroup({}); inviteForm: FormGroup = new FormGroup({});
selectedRoles: Array<string> = []; selectedRoles: Array<string> = [];
selectedLibraries: Array<number> = []; selectedLibraries: Array<number> = [];
emailLink: string = ''; emailLink: string = '';
@ -32,7 +32,7 @@ export class InviteUserComponent implements OnInit {
private confirmService: ConfirmService, private toastr: ToastrService) { } private confirmService: ConfirmService, private toastr: ToastrService) { }
ngOnInit(): void { ngOnInit(): void {
this.inviteForm.addControl('email', new UntypedFormControl('', [Validators.required])); this.inviteForm.addControl('email', new FormControl('', [Validators.required]));
} }
close() { close() {

View File

@ -1,5 +1,5 @@
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { UntypedFormControl, UntypedFormGroup, Validators } from '@angular/forms'; import { FormControl, FormGroup, Validators } from '@angular/forms';
import { ToastrService } from 'ngx-toastr'; import { ToastrService } from 'ngx-toastr';
import { take } from 'rxjs'; import { take } from 'rxjs';
import { SettingsService, EmailTestResult } from '../settings.service'; import { SettingsService, EmailTestResult } from '../settings.service';
@ -13,14 +13,14 @@ import { ServerSettings } from '../_models/server-settings';
export class ManageEmailSettingsComponent implements OnInit { export class ManageEmailSettingsComponent implements OnInit {
serverSettings!: ServerSettings; serverSettings!: ServerSettings;
settingsForm: UntypedFormGroup = new UntypedFormGroup({}); settingsForm: FormGroup = new FormGroup({});
constructor(private settingsService: SettingsService, private toastr: ToastrService) { } constructor(private settingsService: SettingsService, private toastr: ToastrService) { }
ngOnInit(): void { ngOnInit(): void {
this.settingsService.getServerSettings().pipe(take(1)).subscribe((settings: ServerSettings) => { this.settingsService.getServerSettings().pipe(take(1)).subscribe((settings: ServerSettings) => {
this.serverSettings = settings; this.serverSettings = settings;
this.settingsForm.addControl('emailServiceUrl', new UntypedFormControl(this.serverSettings.emailServiceUrl, [Validators.required])); this.settingsForm.addControl('emailServiceUrl', new FormControl(this.serverSettings.emailServiceUrl, [Validators.required]));
}); });
} }

View File

@ -1,5 +1,5 @@
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { UntypedFormGroup, UntypedFormControl, Validators } from '@angular/forms'; import { FormGroup, FormControl, Validators } from '@angular/forms';
import { ToastrService } from 'ngx-toastr'; import { ToastrService } from 'ngx-toastr';
import { take } from 'rxjs'; import { take } from 'rxjs';
import { SettingsService } from '../settings.service'; import { SettingsService } from '../settings.service';
@ -13,14 +13,14 @@ import { ServerSettings } from '../_models/server-settings';
export class ManageMediaSettingsComponent implements OnInit { export class ManageMediaSettingsComponent implements OnInit {
serverSettings!: ServerSettings; serverSettings!: ServerSettings;
settingsForm: UntypedFormGroup = new UntypedFormGroup({}); settingsForm: FormGroup = new FormGroup({});
constructor(private settingsService: SettingsService, private toastr: ToastrService) { } constructor(private settingsService: SettingsService, private toastr: ToastrService) { }
ngOnInit(): void { ngOnInit(): void {
this.settingsService.getServerSettings().pipe(take(1)).subscribe((settings: ServerSettings) => { this.settingsService.getServerSettings().pipe(take(1)).subscribe((settings: ServerSettings) => {
this.serverSettings = settings; this.serverSettings = settings;
this.settingsForm.addControl('convertBookmarkToWebP', new UntypedFormControl(this.serverSettings.convertBookmarkToWebP, [Validators.required])); this.settingsForm.addControl('convertBookmarkToWebP', new FormControl(this.serverSettings.convertBookmarkToWebP, [Validators.required]));
}); });
} }

View File

@ -1,5 +1,5 @@
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { UntypedFormControl, UntypedFormGroup, Validators } from '@angular/forms'; import { FormControl, FormGroup, Validators } from '@angular/forms';
import { ToastrService } from 'ngx-toastr'; import { ToastrService } from 'ngx-toastr';
import { take } from 'rxjs/operators'; import { take } from 'rxjs/operators';
import { ServerService } from 'src/app/_services/server.service'; import { ServerService } from 'src/app/_services/server.service';
@ -14,7 +14,7 @@ import { ServerSettings } from '../_models/server-settings';
}) })
export class ManageSystemComponent implements OnInit { export class ManageSystemComponent implements OnInit {
settingsForm: UntypedFormGroup = new UntypedFormGroup({}); settingsForm: FormGroup = new FormGroup({});
serverSettings!: ServerSettings; serverSettings!: ServerSettings;
serverInfo!: ServerInfo; serverInfo!: ServerInfo;
@ -30,12 +30,12 @@ export class ManageSystemComponent implements OnInit {
this.settingsService.getServerSettings().pipe(take(1)).subscribe((settings: ServerSettings) => { this.settingsService.getServerSettings().pipe(take(1)).subscribe((settings: ServerSettings) => {
this.serverSettings = settings; this.serverSettings = settings;
this.settingsForm.addControl('cacheDirectory', new UntypedFormControl(this.serverSettings.cacheDirectory, [Validators.required])); this.settingsForm.addControl('cacheDirectory', new FormControl(this.serverSettings.cacheDirectory, [Validators.required]));
this.settingsForm.addControl('taskScan', new UntypedFormControl(this.serverSettings.taskScan, [Validators.required])); this.settingsForm.addControl('taskScan', new FormControl(this.serverSettings.taskScan, [Validators.required]));
this.settingsForm.addControl('taskBackup', new UntypedFormControl(this.serverSettings.taskBackup, [Validators.required])); this.settingsForm.addControl('taskBackup', new FormControl(this.serverSettings.taskBackup, [Validators.required]));
this.settingsForm.addControl('port', new UntypedFormControl(this.serverSettings.port, [Validators.required])); this.settingsForm.addControl('port', new FormControl(this.serverSettings.port, [Validators.required]));
this.settingsForm.addControl('loggingLevel', new UntypedFormControl(this.serverSettings.loggingLevel, [Validators.required])); this.settingsForm.addControl('loggingLevel', new FormControl(this.serverSettings.loggingLevel, [Validators.required]));
this.settingsForm.addControl('allowStatCollection', new UntypedFormControl(this.serverSettings.allowStatCollection, [Validators.required])); this.settingsForm.addControl('allowStatCollection', new FormControl(this.serverSettings.allowStatCollection, [Validators.required]));
}); });
} }

View File

@ -1,5 +1,5 @@
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { UntypedFormGroup, UntypedFormControl, Validators } from '@angular/forms'; import { FormGroup, FormControl, Validators } from '@angular/forms';
import { ToastrService } from 'ngx-toastr'; import { ToastrService } from 'ngx-toastr';
import { ConfirmService } from 'src/app/shared/confirm.service'; import { ConfirmService } from 'src/app/shared/confirm.service';
import { SettingsService } from '../settings.service'; import { SettingsService } from '../settings.service';
@ -28,7 +28,7 @@ interface AdhocTask {
export class ManageTasksSettingsComponent implements OnInit { export class ManageTasksSettingsComponent implements OnInit {
serverSettings!: ServerSettings; serverSettings!: ServerSettings;
settingsForm: UntypedFormGroup = new UntypedFormGroup({}); settingsForm: FormGroup = new FormGroup({});
taskFrequencies: Array<string> = []; taskFrequencies: Array<string> = [];
logLevels: Array<string> = []; logLevels: Array<string> = [];
@ -89,8 +89,8 @@ export class ManageTasksSettingsComponent implements OnInit {
this.taskFrequencies = result.frequencies; this.taskFrequencies = result.frequencies;
this.logLevels = result.levels; this.logLevels = result.levels;
this.serverSettings = result.settings; this.serverSettings = result.settings;
this.settingsForm.addControl('taskScan', new UntypedFormControl(this.serverSettings.taskScan, [Validators.required])); this.settingsForm.addControl('taskScan', new FormControl(this.serverSettings.taskScan, [Validators.required]));
this.settingsForm.addControl('taskBackup', new UntypedFormControl(this.serverSettings.taskBackup, [Validators.required])); this.settingsForm.addControl('taskBackup', new FormControl(this.serverSettings.taskBackup, [Validators.required]));
}); });
this.reoccuringTasks$ = this.serverService.getReoccuringJobs().pipe(shareReplay()); this.reoccuringTasks$ = this.serverService.getReoccuringJobs().pipe(shareReplay());

View File

@ -1,6 +1,6 @@
import { DOCUMENT } from '@angular/common'; import { DOCUMENT } from '@angular/common';
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Inject, Input, OnDestroy, OnInit, Output } from '@angular/core'; import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Inject, Input, OnDestroy, OnInit, Output } from '@angular/core';
import { UntypedFormControl, UntypedFormGroup } from '@angular/forms'; import { FormControl, FormGroup } from '@angular/forms';
import { Subject, take, takeUntil } from 'rxjs'; import { Subject, take, takeUntil } from 'rxjs';
import { BookPageLayoutMode } from 'src/app/_models/book-page-layout-mode'; import { BookPageLayoutMode } from 'src/app/_models/book-page-layout-mode';
import { BookTheme } from 'src/app/_models/preferences/book-theme'; import { BookTheme } from 'src/app/_models/preferences/book-theme';
@ -110,7 +110,7 @@ export class ReaderSettingsComponent implements OnInit, OnDestroy {
isFullscreen: boolean = false; isFullscreen: boolean = false;
settingsForm: UntypedFormGroup = new UntypedFormGroup({}); settingsForm: FormGroup = new FormGroup({});
/** /**
* System provided themes * System provided themes
@ -163,7 +163,7 @@ export class ReaderSettingsComponent implements OnInit, OnDestroy {
this.readingDirectionModel = this.user.preferences.bookReaderReadingDirection; this.readingDirectionModel = this.user.preferences.bookReaderReadingDirection;
this.settingsForm.addControl('bookReaderFontFamily', new UntypedFormControl(this.user.preferences.bookReaderFontFamily, [])); this.settingsForm.addControl('bookReaderFontFamily', new FormControl(this.user.preferences.bookReaderFontFamily, []));
this.settingsForm.get('bookReaderFontFamily')!.valueChanges.pipe(takeUntil(this.onDestroy)).subscribe(fontName => { this.settingsForm.get('bookReaderFontFamily')!.valueChanges.pipe(takeUntil(this.onDestroy)).subscribe(fontName => {
const familyName = this.fontFamilies.filter(f => f.title === fontName)[0].family; const familyName = this.fontFamilies.filter(f => f.title === fontName)[0].family;
if (familyName === 'default') { if (familyName === 'default') {
@ -175,36 +175,36 @@ export class ReaderSettingsComponent implements OnInit, OnDestroy {
this.styleUpdate.emit(this.pageStyles); this.styleUpdate.emit(this.pageStyles);
}); });
this.settingsForm.addControl('bookReaderFontSize', new UntypedFormControl(this.user.preferences.bookReaderFontSize, [])); this.settingsForm.addControl('bookReaderFontSize', new FormControl(this.user.preferences.bookReaderFontSize, []));
this.settingsForm.get('bookReaderFontSize')?.valueChanges.pipe(takeUntil(this.onDestroy)).subscribe(value => { this.settingsForm.get('bookReaderFontSize')?.valueChanges.pipe(takeUntil(this.onDestroy)).subscribe(value => {
this.pageStyles['font-size'] = value + '%'; this.pageStyles['font-size'] = value + '%';
this.styleUpdate.emit(this.pageStyles); this.styleUpdate.emit(this.pageStyles);
}); });
this.settingsForm.addControl('bookReaderTapToPaginate', new UntypedFormControl(this.user.preferences.bookReaderTapToPaginate, [])); this.settingsForm.addControl('bookReaderTapToPaginate', new FormControl(this.user.preferences.bookReaderTapToPaginate, []));
this.settingsForm.get('bookReaderTapToPaginate')?.valueChanges.pipe(takeUntil(this.onDestroy)).subscribe(value => { this.settingsForm.get('bookReaderTapToPaginate')?.valueChanges.pipe(takeUntil(this.onDestroy)).subscribe(value => {
this.clickToPaginateChanged.emit(value); this.clickToPaginateChanged.emit(value);
}); });
this.settingsForm.addControl('bookReaderLineSpacing', new UntypedFormControl(this.user.preferences.bookReaderLineSpacing, [])); this.settingsForm.addControl('bookReaderLineSpacing', new FormControl(this.user.preferences.bookReaderLineSpacing, []));
this.settingsForm.get('bookReaderLineSpacing')?.valueChanges.pipe(takeUntil(this.onDestroy)).subscribe(value => { this.settingsForm.get('bookReaderLineSpacing')?.valueChanges.pipe(takeUntil(this.onDestroy)).subscribe(value => {
this.pageStyles['line-height'] = value + '%'; this.pageStyles['line-height'] = value + '%';
this.styleUpdate.emit(this.pageStyles); this.styleUpdate.emit(this.pageStyles);
}); });
this.settingsForm.addControl('bookReaderMargin', new UntypedFormControl(this.user.preferences.bookReaderMargin, [])); this.settingsForm.addControl('bookReaderMargin', new FormControl(this.user.preferences.bookReaderMargin, []));
this.settingsForm.get('bookReaderMargin')?.valueChanges.pipe(takeUntil(this.onDestroy)).subscribe(value => { this.settingsForm.get('bookReaderMargin')?.valueChanges.pipe(takeUntil(this.onDestroy)).subscribe(value => {
this.pageStyles['margin-left'] = value + '%'; this.pageStyles['margin-left'] = value + '%';
this.pageStyles['margin-right'] = value + '%'; this.pageStyles['margin-right'] = value + '%';
this.styleUpdate.emit(this.pageStyles); this.styleUpdate.emit(this.pageStyles);
}); });
this.settingsForm.addControl('layoutMode', new UntypedFormControl(this.user.preferences.bookReaderLayoutMode || BookPageLayoutMode.Default, [])); this.settingsForm.addControl('layoutMode', new FormControl(this.user.preferences.bookReaderLayoutMode || BookPageLayoutMode.Default, []));
this.settingsForm.get('layoutMode')?.valueChanges.pipe(takeUntil(this.onDestroy)).subscribe((layoutMode: BookPageLayoutMode) => { this.settingsForm.get('layoutMode')?.valueChanges.pipe(takeUntil(this.onDestroy)).subscribe((layoutMode: BookPageLayoutMode) => {
this.layoutModeUpdate.emit(layoutMode); this.layoutModeUpdate.emit(layoutMode);
}); });
this.settingsForm.addControl('bookReaderImmersiveMode', new UntypedFormControl(this.user.preferences.bookReaderImmersiveMode, [])); this.settingsForm.addControl('bookReaderImmersiveMode', new FormControl(this.user.preferences.bookReaderImmersiveMode, []));
this.settingsForm.get('bookReaderImmersiveMode')?.valueChanges.pipe(takeUntil(this.onDestroy)).subscribe((immersiveMode: boolean) => { this.settingsForm.get('bookReaderImmersiveMode')?.valueChanges.pipe(takeUntil(this.onDestroy)).subscribe((immersiveMode: boolean) => {
if (immersiveMode) { if (immersiveMode) {
this.settingsForm.get('bookReaderTapToPaginate')?.setValue(true); this.settingsForm.get('bookReaderTapToPaginate')?.setValue(true);

View File

@ -1,5 +1,5 @@
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, Input, OnInit, ViewChild, ViewEncapsulation } from '@angular/core'; import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, Input, OnInit, ViewChild, ViewEncapsulation } from '@angular/core';
import { UntypedFormGroup, UntypedFormControl } from '@angular/forms'; import { FormGroup, FormControl } from '@angular/forms';
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
import { ToastrService } from 'ngx-toastr'; import { ToastrService } from 'ngx-toastr';
import { CollectionTag } from 'src/app/_models/collection-tag'; import { CollectionTag } from 'src/app/_models/collection-tag';
@ -26,7 +26,7 @@ export class BulkAddToCollectionComponent implements OnInit {
*/ */
lists: Array<CollectionTag> = []; lists: Array<CollectionTag> = [];
loading: boolean = false; loading: boolean = false;
listForm: UntypedFormGroup = new UntypedFormGroup({}); listForm: FormGroup = new FormGroup({});
collectionTitleTrackby = (index: number, item: CollectionTag) => `${item.title}`; collectionTitleTrackby = (index: number, item: CollectionTag) => `${item.title}`;
@ -38,8 +38,8 @@ export class BulkAddToCollectionComponent implements OnInit {
ngOnInit(): void { ngOnInit(): void {
this.listForm.addControl('title', new UntypedFormControl(this.title, [])); this.listForm.addControl('title', new FormControl(this.title, []));
this.listForm.addControl('filterQuery', new UntypedFormControl('', [])); this.listForm.addControl('filterQuery', new FormControl('', []));
this.loading = true; this.loading = true;
this.cdRef.markForCheck(); this.cdRef.markForCheck();

View File

@ -1,5 +1,5 @@
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnInit } from '@angular/core'; import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnInit } from '@angular/core';
import { UntypedFormControl, UntypedFormGroup } from '@angular/forms'; import { FormControl, FormGroup } from '@angular/forms';
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
import { ToastrService } from 'ngx-toastr'; import { ToastrService } from 'ngx-toastr';
import { forkJoin } from 'rxjs'; import { forkJoin } from 'rxjs';
@ -37,7 +37,7 @@ export class EditCollectionTagsComponent implements OnInit {
pagination!: Pagination; pagination!: Pagination;
selectAll: boolean = true; selectAll: boolean = true;
libraryNames!: any; libraryNames!: any;
collectionTagForm!: UntypedFormGroup; collectionTagForm!: FormGroup;
tabs = [{title: 'General', id: TabID.General}, {title: 'Cover Image', id: TabID.CoverImage}]; tabs = [{title: 'General', id: TabID.General}, {title: 'Cover Image', id: TabID.CoverImage}];
active = TabID.General; active = TabID.General;
imageUrls: Array<string> = []; imageUrls: Array<string> = [];
@ -65,10 +65,10 @@ export class EditCollectionTagsComponent implements OnInit {
if (this.pagination == undefined) { if (this.pagination == undefined) {
this.pagination = {totalPages: 1, totalItems: 200, itemsPerPage: 200, currentPage: 0}; this.pagination = {totalPages: 1, totalItems: 200, itemsPerPage: 200, currentPage: 0};
} }
this.collectionTagForm = new UntypedFormGroup({ this.collectionTagForm = new FormGroup({
summary: new UntypedFormControl(this.tag.summary, []), summary: new FormControl(this.tag.summary, []),
coverImageLocked: new UntypedFormControl(this.tag.coverImageLocked, []), coverImageLocked: new FormControl(this.tag.coverImageLocked, []),
coverImageIndex: new UntypedFormControl(0, []), coverImageIndex: new FormControl(0, []),
}); });
this.imageUrls.push(this.imageService.randomize(this.imageService.getCollectionCoverImage(this.tag.id))); this.imageUrls.push(this.imageService.randomize(this.imageService.getCollectionCoverImage(this.tag.id)));

View File

@ -37,5 +37,17 @@
(selection)="bulkSelectionService.handleCardSelection('series', position, series.length, $event)" [selected]="bulkSelectionService.isCardSelected('series', position)" [allowSelection]="true" (selection)="bulkSelectionService.handleCardSelection('series', position, series.length, $event)" [selected]="bulkSelectionService.isCardSelected('series', position)" [allowSelection]="true"
></app-series-card> ></app-series-card>
</ng-template> </ng-template>
<div *ngIf="!filterActive && series.length === 0">
<ng-template #noData>
There are no items. Try adding a series.
</ng-template>
</div>
<div *ngIf="filterActive && series.length === 0">
<ng-template #noData>
No items match your current filter.
</ng-template>
</div>
</app-card-detail-layout> </app-card-detail-layout>
</div> </div>

View File

@ -1,5 +1,5 @@
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ContentChild, EventEmitter, Input, OnDestroy, OnInit, Output } from '@angular/core'; import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ContentChild, EventEmitter, Input, OnDestroy, OnInit, Output } from '@angular/core';
import { UntypedFormControl, UntypedFormGroup } from '@angular/forms'; import { FormControl, FormGroup } from '@angular/forms';
import { NgbCollapse } from '@ng-bootstrap/ng-bootstrap'; import { NgbCollapse } from '@ng-bootstrap/ng-bootstrap';
import { distinctUntilChanged, forkJoin, map, Observable, of, ReplaySubject, Subject, takeUntil } from 'rxjs'; import { distinctUntilChanged, forkJoin, map, Observable, of, ReplaySubject, Subject, takeUntil } from 'rxjs';
import { UtilityService } from '../shared/_services/utility.service'; import { UtilityService } from '../shared/_services/utility.service';
@ -66,9 +66,9 @@ export class MetadataFilterComponent implements OnInit, OnDestroy {
libraries: Array<FilterItem<Library>> = []; libraries: Array<FilterItem<Library>> = [];
readProgressGroup!: UntypedFormGroup; readProgressGroup!: FormGroup;
sortGroup!: UntypedFormGroup; sortGroup!: FormGroup;
seriesNameGroup!: UntypedFormGroup; seriesNameGroup!: FormGroup;
isAscendingSort: boolean = true; isAscendingSort: boolean = true;
updateApplied: number = 0; updateApplied: number = 0;
@ -106,18 +106,18 @@ export class MetadataFilterComponent implements OnInit, OnDestroy {
} }
this.filter = this.seriesService.createSeriesFilter(); this.filter = this.seriesService.createSeriesFilter();
this.readProgressGroup = new UntypedFormGroup({ this.readProgressGroup = new FormGroup({
read: new UntypedFormControl({value: this.filter.readStatus.read, disabled: this.filterSettings.readProgressDisabled}, []), read: new FormControl({value: this.filter.readStatus.read, disabled: this.filterSettings.readProgressDisabled}, []),
notRead: new UntypedFormControl({value: this.filter.readStatus.notRead, disabled: this.filterSettings.readProgressDisabled}, []), notRead: new FormControl({value: this.filter.readStatus.notRead, disabled: this.filterSettings.readProgressDisabled}, []),
inProgress: new UntypedFormControl({value: this.filter.readStatus.inProgress, disabled: this.filterSettings.readProgressDisabled}, []), inProgress: new FormControl({value: this.filter.readStatus.inProgress, disabled: this.filterSettings.readProgressDisabled}, []),
}); });
this.sortGroup = new UntypedFormGroup({ this.sortGroup = new FormGroup({
sortField: new UntypedFormControl({value: this.filter.sortOptions?.sortField || SortField.SortName, disabled: this.filterSettings.sortDisabled}, []), sortField: new FormControl({value: this.filter.sortOptions?.sortField || SortField.SortName, disabled: this.filterSettings.sortDisabled}, []),
}); });
this.seriesNameGroup = new UntypedFormGroup({ this.seriesNameGroup = new FormGroup({
seriesNameQuery: new UntypedFormControl({value: this.filter.seriesNameQuery || '', disabled: this.filterSettings.searchNameDisabled}, []) seriesNameQuery: new FormControl({value: this.filter.seriesNameQuery || '', disabled: this.filterSettings.searchNameDisabled}, [])
}); });
this.readProgressGroup.valueChanges.pipe(takeUntil(this.onDestroy)).subscribe(changes => { this.readProgressGroup.valueChanges.pipe(takeUntil(this.onDestroy)).subscribe(changes => {

View File

@ -1,5 +1,5 @@
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ContentChild, ElementRef, EventEmitter, HostListener, Input, OnDestroy, OnInit, Output, TemplateRef, ViewChild } from '@angular/core'; import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ContentChild, ElementRef, EventEmitter, HostListener, Input, OnDestroy, OnInit, Output, TemplateRef, ViewChild } from '@angular/core';
import { UntypedFormControl, UntypedFormGroup } from '@angular/forms'; import { FormControl, FormGroup } from '@angular/forms';
import { Subject } from 'rxjs'; import { Subject } from 'rxjs';
import { debounceTime, takeUntil } from 'rxjs/operators'; import { debounceTime, takeUntil } from 'rxjs/operators';
import { KEY_CODES } from '../../shared/_services/utility.service'; import { KEY_CODES } from '../../shared/_services/utility.service';
@ -66,7 +66,7 @@ export class GroupedTypeaheadComponent implements OnInit, OnDestroy {
hasFocus: boolean = false; hasFocus: boolean = false;
isLoading: boolean = false; isLoading: boolean = false;
typeaheadForm: UntypedFormGroup = new UntypedFormGroup({}); typeaheadForm: FormGroup = new FormGroup({});
prevSearchTerm: string = ''; prevSearchTerm: string = '';
@ -106,7 +106,7 @@ export class GroupedTypeaheadComponent implements OnInit, OnDestroy {
} }
ngOnInit(): void { ngOnInit(): void {
this.typeaheadForm.addControl('typeahead', new UntypedFormControl(this.initialValue, [])); this.typeaheadForm.addControl('typeahead', new FormControl(this.initialValue, []));
this.cdRef.markForCheck(); this.cdRef.markForCheck();
this.typeaheadForm.valueChanges.pipe(debounceTime(this.debounceTime), takeUntil(this.onDestroy)).subscribe(change => { this.typeaheadForm.valueChanges.pipe(debounceTime(this.debounceTime), takeUntil(this.onDestroy)).subscribe(change => {

View File

@ -1,5 +1,5 @@
import { AfterViewInit, Component, ElementRef, Input, OnInit, ViewChild } from '@angular/core'; import { AfterViewInit, Component, ElementRef, Input, OnInit, ViewChild } from '@angular/core';
import { UntypedFormControl, UntypedFormGroup } from '@angular/forms'; import { FormControl, FormGroup } from '@angular/forms';
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
import { ToastrService } from 'ngx-toastr'; import { ToastrService } from 'ngx-toastr';
import { ReadingList } from 'src/app/_models/reading-list'; import { ReadingList } from 'src/app/_models/reading-list';
@ -56,7 +56,7 @@ export class AddToListModalComponent implements OnInit, AfterViewInit {
*/ */
lists: Array<any> = []; lists: Array<any> = [];
loading: boolean = false; loading: boolean = false;
listForm: UntypedFormGroup = new UntypedFormGroup({}); listForm: FormGroup = new FormGroup({});
@ViewChild('title') inputElem!: ElementRef<HTMLInputElement>; @ViewChild('title') inputElem!: ElementRef<HTMLInputElement>;
@ -65,8 +65,8 @@ export class AddToListModalComponent implements OnInit, AfterViewInit {
ngOnInit(): void { ngOnInit(): void {
this.listForm.addControl('title', new UntypedFormControl(this.title, [])); this.listForm.addControl('title', new FormControl(this.title, []));
this.listForm.addControl('filterQuery', new UntypedFormControl('', [])); this.listForm.addControl('filterQuery', new FormControl('', []));
this.loading = true; this.loading = true;
this.readingListService.getReadingLists(false).subscribe(lists => { this.readingListService.getReadingLists(false).subscribe(lists => {

View File

@ -1,5 +1,5 @@
import { Component, Input, OnInit } from '@angular/core'; import { Component, Input, OnInit } from '@angular/core';
import { UntypedFormGroup, UntypedFormControl, Validators } from '@angular/forms'; import { FormGroup, FormControl, Validators } from '@angular/forms';
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
import { ToastrService } from 'ngx-toastr'; import { ToastrService } from 'ngx-toastr';
import { forkJoin } from 'rxjs'; import { forkJoin } from 'rxjs';
@ -18,7 +18,7 @@ import { UploadService } from 'src/app/_services/upload.service';
export class EditReadingListModalComponent implements OnInit { export class EditReadingListModalComponent implements OnInit {
@Input() readingList!: ReadingList; @Input() readingList!: ReadingList;
reviewGroup!: UntypedFormGroup; reviewGroup!: FormGroup;
coverImageIndex: number = 0; coverImageIndex: number = 0;
/** /**
@ -41,9 +41,9 @@ export class EditReadingListModalComponent implements OnInit {
private imageService: ImageService) { } private imageService: ImageService) { }
ngOnInit(): void { ngOnInit(): void {
this.reviewGroup = new UntypedFormGroup({ this.reviewGroup = new FormGroup({
title: new UntypedFormControl(this.readingList.title, [Validators.required]), title: new FormControl(this.readingList.title, [Validators.required]),
summary: new UntypedFormControl(this.readingList.summary, []) summary: new FormControl(this.readingList.summary, [])
}); });
this.imageUrls.push(this.imageService.randomize(this.imageService.getReadingListCoverImage(this.readingList.id))); this.imageUrls.push(this.imageService.randomize(this.imageService.getReadingListCoverImage(this.readingList.id)));

View File

@ -1,5 +1,5 @@
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnInit } from '@angular/core'; import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnInit } from '@angular/core';
import { UntypedFormControl, UntypedFormGroup, Validators } from '@angular/forms'; import { FormControl, FormGroup, Validators } from '@angular/forms';
import { SafeUrl } from '@angular/platform-browser'; import { SafeUrl } from '@angular/platform-browser';
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
import { ToastrService } from 'ngx-toastr'; import { ToastrService } from 'ngx-toastr';
@ -17,7 +17,7 @@ export class AddEmailToAccountMigrationModalComponent implements OnInit {
@Input() password!: string; @Input() password!: string;
isSaving: boolean = false; isSaving: boolean = false;
registerForm: UntypedFormGroup = new UntypedFormGroup({}); registerForm: FormGroup = new FormGroup({});
emailLink: string = ''; emailLink: string = '';
emailLinkUrl: SafeUrl | undefined; emailLinkUrl: SafeUrl | undefined;
error: string = ''; error: string = '';
@ -27,9 +27,9 @@ export class AddEmailToAccountMigrationModalComponent implements OnInit {
} }
ngOnInit(): void { ngOnInit(): void {
this.registerForm.addControl('username', new UntypedFormControl(this.username, [Validators.required])); this.registerForm.addControl('username', new FormControl(this.username, [Validators.required]));
this.registerForm.addControl('email', new UntypedFormControl('', [Validators.required, Validators.email])); this.registerForm.addControl('email', new FormControl('', [Validators.required, Validators.email]));
this.registerForm.addControl('password', new UntypedFormControl(this.password, [Validators.required])); this.registerForm.addControl('password', new FormControl(this.password, [Validators.required]));
this.cdRef.markForCheck(); this.cdRef.markForCheck();
} }

View File

@ -1,5 +1,5 @@
import { ChangeDetectionStrategy, ChangeDetectorRef, Component } from '@angular/core'; import { ChangeDetectionStrategy, ChangeDetectorRef, Component } from '@angular/core';
import { UntypedFormControl, UntypedFormGroup, Validators } from '@angular/forms'; import { FormControl, FormGroup, Validators } from '@angular/forms';
import { ActivatedRoute, Router } from '@angular/router'; import { ActivatedRoute, Router } from '@angular/router';
import { ToastrService } from 'ngx-toastr'; import { ToastrService } from 'ngx-toastr';
import { ThemeService } from 'src/app/_services/theme.service'; import { ThemeService } from 'src/app/_services/theme.service';
@ -18,10 +18,10 @@ export class ConfirmEmailComponent {
*/ */
token: string = ''; token: string = '';
registerForm: UntypedFormGroup = new UntypedFormGroup({ registerForm: FormGroup = new FormGroup({
email: new UntypedFormControl('', [Validators.required, Validators.email]), email: new FormControl('', [Validators.required, Validators.email]),
username: new UntypedFormControl('', [Validators.required]), username: new FormControl('', [Validators.required]),
password: new UntypedFormControl('', [Validators.required, Validators.maxLength(32), Validators.minLength(6)]), password: new FormControl('', [Validators.required, Validators.maxLength(32), Validators.minLength(6)]),
}); });
/** /**

View File

@ -1,7 +1,7 @@
<app-splash-container> <app-splash-container>
<ng-container title><h2>Password Reset</h2></ng-container> <ng-container title><h2>Password Reset</h2></ng-container>
<ng-container body> <ng-container body>
<p>Enter the email of your account. We will send you an email </p> <p>Enter the new password</p>
<form [formGroup]="registerForm" (ngSubmit)="submit()"> <form [formGroup]="registerForm" (ngSubmit)="submit()">
<div class="mb-3"> <div class="mb-3">
<label for="password" class="form-label">Password</label>&nbsp;<i class="fa fa-info-circle" placement="right" [ngbTooltip]="passwordTooltip" role="button" tabindex="0"></i> <label for="password" class="form-label">Password</label>&nbsp;<i class="fa fa-info-circle" placement="right" [ngbTooltip]="passwordTooltip" role="button" tabindex="0"></i>

View File

@ -0,0 +1,4 @@
input {
background-color: #fff !important;
color: black !important;
}

View File

@ -1,8 +1,9 @@
import { ChangeDetectionStrategy, ChangeDetectorRef, Component } from '@angular/core'; import { ChangeDetectionStrategy, ChangeDetectorRef, Component } from '@angular/core';
import { UntypedFormGroup, UntypedFormControl, Validators } from '@angular/forms'; import { FormGroup, FormControl, Validators } from '@angular/forms';
import { ActivatedRoute, Router } from '@angular/router'; import { ActivatedRoute, Router } from '@angular/router';
import { ToastrService } from 'ngx-toastr'; import { ToastrService } from 'ngx-toastr';
import { AccountService } from 'src/app/_services/account.service'; import { AccountService } from 'src/app/_services/account.service';
import { NavService } from 'src/app/_services/nav.service';
@Component({ @Component({
selector: 'app-confirm-reset-password', selector: 'app-confirm-reset-password',
@ -13,14 +14,18 @@ import { AccountService } from 'src/app/_services/account.service';
export class ConfirmResetPasswordComponent { export class ConfirmResetPasswordComponent {
token: string = ''; token: string = '';
registerForm: UntypedFormGroup = new UntypedFormGroup({ registerForm: FormGroup = new FormGroup({
email: new UntypedFormControl('', [Validators.required, Validators.email]), email: new FormControl('', [Validators.required, Validators.email]),
password: new UntypedFormControl('', [Validators.required, Validators.maxLength(32), Validators.minLength(6)]), password: new FormControl('', [Validators.required, Validators.maxLength(32), Validators.minLength(6)]),
}); });
constructor(private route: ActivatedRoute, private router: Router, constructor(private route: ActivatedRoute, private router: Router,
private accountService: AccountService, private toastr: ToastrService, private accountService: AccountService, private toastr: ToastrService,
private readonly cdRef: ChangeDetectorRef) { private readonly cdRef: ChangeDetectorRef, private navService: NavService) {
this.navService.showNavBar();
this.navService.hideSideNav();
const token = this.route.snapshot.queryParamMap.get('token'); const token = this.route.snapshot.queryParamMap.get('token');
const email = this.route.snapshot.queryParamMap.get('email'); const email = this.route.snapshot.queryParamMap.get('email');

View File

@ -1,5 +1,5 @@
import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core'; import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
import { UntypedFormGroup, UntypedFormControl, Validators } from '@angular/forms'; import { FormGroup, FormControl, Validators } from '@angular/forms';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { ToastrService } from 'ngx-toastr'; import { ToastrService } from 'ngx-toastr';
import { take } from 'rxjs/operators'; import { take } from 'rxjs/operators';
@ -17,10 +17,10 @@ import { MemberService } from 'src/app/_services/member.service';
}) })
export class RegisterComponent implements OnInit { export class RegisterComponent implements OnInit {
registerForm: UntypedFormGroup = new UntypedFormGroup({ registerForm: FormGroup = new FormGroup({
email: new UntypedFormControl('', [Validators.required, Validators.email]), email: new FormControl('', [Validators.required, Validators.email]),
username: new UntypedFormControl('', [Validators.required]), username: new FormControl('', [Validators.required]),
password: new UntypedFormControl('', [Validators.required, Validators.maxLength(32), Validators.minLength(6)]), password: new FormControl('', [Validators.required, Validators.maxLength(32), Validators.minLength(6)]),
}); });
constructor(private router: Router, private accountService: AccountService, constructor(private router: Router, private accountService: AccountService,

View File

@ -1,5 +1,5 @@
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnInit } from '@angular/core'; import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnInit } from '@angular/core';
import { UntypedFormGroup, UntypedFormControl, Validators } from '@angular/forms'; import { FormGroup, FormControl, Validators } from '@angular/forms';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
import { ToastrService } from 'ngx-toastr'; import { ToastrService } from 'ngx-toastr';
@ -20,9 +20,9 @@ import { NavService } from '../../_services/nav.service';
export class UserLoginComponent implements OnInit { export class UserLoginComponent implements OnInit {
//model: any = {username: '', password: ''}; //model: any = {username: '', password: ''};
loginForm: UntypedFormGroup = new UntypedFormGroup({ loginForm: FormGroup = new FormGroup({
username: new UntypedFormControl('', [Validators.required]), username: new FormControl('', [Validators.required]),
password: new UntypedFormControl('', [Validators.required]) password: new FormControl('', [Validators.required])
}); });
/** /**

View File

@ -1,5 +1,5 @@
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnInit } from '@angular/core'; import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnInit } from '@angular/core';
import { UntypedFormControl, UntypedFormGroup } from '@angular/forms'; import { FormControl, FormGroup } from '@angular/forms';
import { NgbModal, NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; import { NgbModal, NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
import { Series } from 'src/app/_models/series'; import { Series } from 'src/app/_models/series';
import { SeriesService } from 'src/app/_services/series.service'; import { SeriesService } from 'src/app/_services/series.service';
@ -13,14 +13,14 @@ import { SeriesService } from 'src/app/_services/series.service';
export class ReviewSeriesModalComponent implements OnInit { export class ReviewSeriesModalComponent implements OnInit {
@Input() series!: Series; @Input() series!: Series;
reviewGroup!: UntypedFormGroup; reviewGroup!: FormGroup;
constructor(public modal: NgbActiveModal, private seriesService: SeriesService, private readonly cdRef: ChangeDetectorRef) {} constructor(public modal: NgbActiveModal, private seriesService: SeriesService, private readonly cdRef: ChangeDetectorRef) {}
ngOnInit(): void { ngOnInit(): void {
this.reviewGroup = new UntypedFormGroup({ this.reviewGroup = new FormGroup({
review: new UntypedFormControl(this.series.userReview, []), review: new FormControl(this.series.userReview, []),
rating: new UntypedFormControl(this.series.userRating, []) rating: new FormControl(this.series.userRating, [])
}); });
this.cdRef.markForCheck(); this.cdRef.markForCheck();
} }

View File

@ -35,7 +35,7 @@ import { NavService } from '../_services/nav.service';
import { RelatedSeries } from '../_models/series-detail/related-series'; import { RelatedSeries } from '../_models/series-detail/related-series';
import { RelationKind } from '../_models/series-detail/relation-kind'; import { RelationKind } from '../_models/series-detail/relation-kind';
import { CardDetailDrawerComponent } from '../cards/card-detail-drawer/card-detail-drawer.component'; import { CardDetailDrawerComponent } from '../cards/card-detail-drawer/card-detail-drawer.component';
import { FormGroup, UntypedFormControl, UntypedFormGroup } from '@angular/forms'; import { FormGroup, FormControl } from '@angular/forms';
import { PageLayoutMode } from '../_models/page-layout-mode'; import { PageLayoutMode } from '../_models/page-layout-mode';
import { DOCUMENT } from '@angular/common'; import { DOCUMENT } from '@angular/common';
import { User } from '../_models/user'; import { User } from '../_models/user';
@ -151,8 +151,8 @@ export class SeriesDetailComponent implements OnInit, OnDestroy, AfterContentChe
renderMode: PageLayoutMode = PageLayoutMode.Cards; renderMode: PageLayoutMode = PageLayoutMode.Cards;
pageExtrasGroup = new FormGroup({ pageExtrasGroup = new FormGroup({
'sortingOption': new UntypedFormControl(this.sortingOptions[0].value, []), 'sortingOption': new FormControl(this.sortingOptions[0].value, []),
'renderMode': new UntypedFormControl(this.renderMode, []), 'renderMode': new FormControl(this.renderMode, []),
}); });
isAscendingSort: boolean = false; // TODO: Get this from User preferences isAscendingSort: boolean = false; // TODO: Get this from User preferences
@ -299,7 +299,8 @@ export class SeriesDetailComponent implements OnInit, OnDestroy, AfterContentChe
this.changeDetectionRef.markForCheck(); this.changeDetectionRef.markForCheck();
this.loadSeries(this.seriesId); this.loadSeries(this.seriesId);
this.pageExtrasGroup.get('renderMode')?.valueChanges.pipe(takeUntil(this.onDestroy)).subscribe((val: PageLayoutMode) => { this.pageExtrasGroup.get('renderMode')?.valueChanges.pipe(takeUntil(this.onDestroy)).subscribe((val: PageLayoutMode | null) => {
if (val == null) return;
this.renderMode = val; this.renderMode = val;
this.changeDetectionRef.markForCheck(); this.changeDetectionRef.markForCheck();
}); });

View File

@ -1,5 +1,5 @@
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { UntypedFormControl } from '@angular/forms'; import { FormControl } from '@angular/forms';
export type SelectionCompareFn<T> = (a: T, b: T) => boolean; export type SelectionCompareFn<T> = (a: T, b: T) => boolean;
@ -48,7 +48,7 @@ export class TypeaheadSettings<T> {
/** /**
* Optional form Control to tie model to. * Optional form Control to tie model to.
*/ */
formControl?: UntypedFormControl; formControl?: FormControl;
/** /**
* If true, typeahead will remove already selected items from fetchFn results. Only appies when multiple=true * If true, typeahead will remove already selected items from fetchFn results. Only appies when multiple=true
*/ */

View File

@ -1,7 +1,7 @@
import { trigger, state, style, transition, animate } from '@angular/animations'; import { trigger, state, style, transition, animate } from '@angular/animations';
import { DOCUMENT } from '@angular/common'; import { DOCUMENT } from '@angular/common';
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ContentChild, ElementRef, EventEmitter, HostListener, Inject, Input, OnDestroy, OnInit, Output, Renderer2, RendererStyleFlags2, TemplateRef, ViewChild } from '@angular/core'; import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ContentChild, ElementRef, EventEmitter, HostListener, Inject, Input, OnDestroy, OnInit, Output, Renderer2, RendererStyleFlags2, TemplateRef, ViewChild } from '@angular/core';
import { UntypedFormControl, UntypedFormGroup } from '@angular/forms'; import { FormControl, FormGroup } from '@angular/forms';
import { Observable, of, ReplaySubject, Subject } from 'rxjs'; import { Observable, of, ReplaySubject, Subject } from 'rxjs';
import { auditTime, distinctUntilChanged, filter, map, shareReplay, switchMap, take, takeUntil, tap } from 'rxjs/operators'; import { auditTime, distinctUntilChanged, filter, map, shareReplay, switchMap, take, takeUntil, tap } from 'rxjs/operators';
import { KEY_CODES } from '../shared/_services/utility.service'; import { KEY_CODES } from '../shared/_services/utility.service';
@ -189,8 +189,8 @@ export class TypeaheadComponent implements OnInit, OnDestroy {
showAddItem: boolean = false; showAddItem: boolean = false;
filteredOptions!: Observable<string[]>; filteredOptions!: Observable<string[]>;
isLoadingOptions: boolean = false; isLoadingOptions: boolean = false;
typeaheadControl!: UntypedFormControl; typeaheadControl!: FormControl;
typeaheadForm!: UntypedFormGroup; typeaheadForm!: FormGroup;
private readonly onDestroy = new Subject<void>(); private readonly onDestroy = new Subject<void>();
@ -230,9 +230,9 @@ export class TypeaheadComponent implements OnInit, OnDestroy {
if (this.settings.hasOwnProperty('formControl') && this.settings.formControl) { if (this.settings.hasOwnProperty('formControl') && this.settings.formControl) {
this.typeaheadControl = this.settings.formControl; this.typeaheadControl = this.settings.formControl;
} else { } else {
this.typeaheadControl = new UntypedFormControl(''); this.typeaheadControl = new FormControl('');
} }
this.typeaheadForm = new UntypedFormGroup({ this.typeaheadForm = new FormGroup({
'typeahead': this.typeaheadControl 'typeahead': this.typeaheadControl
}); });