Release Shakeout 3 (#1597)

* Fixed a bug where bulk selection on series detail wouldn't allow you to select the whole card, only the checkbox.

* Refactored the implementation of MarkChaptersAsRead to streamline it.

* Fixed a bug where volume cards weren't properly updating their read state based on events from backend.

* Added [ScannerService] to more loggers

* Fixed invite user flow

* Fixed broken edit user flow

* Fixed calling device service on unauthenticated screens causing redirection

* Fixed reset password via email not working when success message was sent back

* Fixed broken white theme on book reader

* Small tweaks to white theme

* More fixes

* Adjusted AutomaticRetries
This commit is contained in:
Joe Milazzo 2022-10-20 16:39:42 -07:00 committed by GitHub
parent b396217e7d
commit dbe1152d87
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 224 additions and 66 deletions

View file

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

View file

@ -1,9 +1,10 @@
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { ReplaySubject, shareReplay, switchMap, take, tap } from 'rxjs';
import { ReplaySubject, shareReplay, take, tap } from 'rxjs';
import { environment } from 'src/environments/environment';
import { Device } from '../_models/device/device';
import { DevicePlatform } from '../_models/device/device-platform';
import { AccountService } from './account.service';
@Injectable({
providedIn: 'root'
@ -16,9 +17,14 @@ export class DeviceService {
public devices$ = this.devicesSource.asObservable().pipe(shareReplay());
constructor(private httpClient: HttpClient) {
this.httpClient.get<Device[]>(this.baseUrl + 'device', {}).subscribe(data => {
this.devicesSource.next(data);
constructor(private httpClient: HttpClient, private accountService: AccountService) {
// Ensure we are authenticated before we make an authenticated api call.
this.accountService.currentUser$.pipe(take(1)).subscribe(user => {
if (!user) return;
this.httpClient.get<Device[]>(this.baseUrl + 'device', {}).subscribe(data => {
this.devicesSource.next(data);
});
});
}

View file

@ -34,6 +34,7 @@ export class EditUserComponent implements OnInit {
this.userForm.addControl('username', new FormControl(this.member.username, [Validators.required]));
this.userForm.get('email')?.disable();
this.selectedRestriction = this.member.ageRestriction;
}
updateRoleSelection(roles: Array<string>) {

View file

@ -85,7 +85,6 @@ export const BookBlackTheme = `
--br-actionbar-button-hover-border-color: #6c757d;
--br-actionbar-bg-color: black;
}
}

View file

@ -1,15 +1,140 @@
// Important note about themes. Must have one section with .reader-container that contains color, background-color and rest of the styles must be scoped to .book-content
export const BookWhiteTheme = `
:root .brtheme-white {
--drawer-text-color: white;
--br-actionbar-bg-color: white;
--bs-btn-active-color: black;
--progress-bg-color: rgb(222, 226, 230);
/* General */
--color-scheme: light;
--bs-body-color: black;
--hr-color: rgba(239, 239, 239, 0.125);
--accent-bg-color: rgba(1, 4, 9, 0.5);
--accent-text-color: lightgrey;
--body-text-color: black;
--btn-icon-filter: invert(1) grayscale(100%) brightness(200%);
/* Drawer */
--drawer-bg-color: white;
--drawer-text-color: black;
--drawer-pagination-horizontal-rule: inset 0 -1px 0 rgb(255 255 255 / 20%);
--drawer-pagination-border: 1px solid rgb(0 0 0 / 13%);
/* Accordion */
--accordion-header-text-color: rgba(74, 198, 148, 0.9);
--accordion-header-bg-color: rgba(52, 60, 70, 0.5);
--accordion-body-bg-color: white;
--accordion-body-border-color: rgba(239, 239, 239, 0.125);
--accordion-body-text-color: var(--body-text-color);
--accordion-header-collapsed-text-color: rgba(74, 198, 148, 0.9);
--accordion-header-collapsed-bg-color: white;
--accordion-button-focus-border-color: unset;
--accordion-button-focus-box-shadow: unset;
--accordion-active-body-bg-color: white;
/* Buttons */
--btn-focus-boxshadow-color: rgb(255 255 255 / 50%);
--btn-primary-text-color: white;
--btn-primary-bg-color: var(--primary-color);
--btn-primary-border-color: var(--primary-color);
--btn-primary-hover-text-color: white;
--btn-primary-hover-bg-color: var(--primary-color-darker-shade);
--btn-primary-hover-border-color: var(--primary-color-darker-shade);
--btn-alt-bg-color: #424c72;
--btn-alt-border-color: #444f75;
--btn-alt-hover-bg-color: #3b4466;
--btn-alt-focus-bg-color: #343c59;
--btn-alt-focus-boxshadow-color: rgb(255 255 255 / 50%);
--btn-fa-icon-color: black;
--btn-disabled-bg-color: #343a40;
--btn-disabled-text-color: #efefef;
--btn-disabled-border-color: #6c757d;
/* Inputs */
--input-bg-color: white;
--input-bg-readonly-color: white;
--input-focused-border-color: #ccc;
--input-text-color: black;
--input-placeholder-color: black;
--input-border-color: #ccc;
--input-focus-boxshadow-color: rgb(255 255 255 / 50%);
/* Nav (Tabs) */
--nav-tab-border-color: rgba(44, 118, 88, 0.7);
--nav-tab-text-color: var(--body-text-color);
--nav-tab-bg-color: var(--primary-color);
--nav-tab-hover-border-color: var(--primary-color);
--nav-tab-active-text-color: white;
--nav-tab-border-hover-color: transparent;
--nav-tab-hover-text-color: var(--body-text-color);
--nav-tab-hover-bg-color: transparent;
--nav-tab-border-top: rgba(44, 118, 88, 0.7);
--nav-tab-border-left: rgba(44, 118, 88, 0.7);
--nav-tab-border-bottom: rgba(44, 118, 88, 0.7);
--nav-tab-border-right: rgba(44, 118, 88, 0.7);
--nav-tab-hover-border-top: rgba(44, 118, 88, 0.7);
--nav-tab-hover-border-left: rgba(44, 118, 88, 0.7);
--nav-tab-hover-border-bottom: var(--bs-body-bg);
--nav-tab-hover-border-right: rgba(44, 118, 88, 0.7);
--nav-tab-active-hover-bg-color: var(--primary-color);
--nav-link-bg-color: var(--primary-color);
--nav-link-active-text-color: white;
--nav-link-text-color: white;
/* Reading Bar */
--br-actionbar-button-text-color: black;
--br-actionbar-button-hover-border-color: #6c757d;
--br-actionbar-bg-color: white;
/* Drawer */
--drawer-pagination-horizontal-rule: inset 0 -1px 0 rgb(0 0 0 / 13%);
--drawer-pagination-border: 1px solid rgb(0 0 0 / 13%);
}
.reader-container {
color: black !important;
background-image: none !important;
background-color: white !important;
}
}
.reader-container {
color: black !important;
background-image: none !important;
background-color: white !important;
}
.book-content *:not(input), .book-content *:not(select), .book-content *:not(code), .book-content *:not(:link), .book-content *:not(.ngx-toastr) {
color: #dcdcdc !important;
}
.book-content code {
color: #e83e8c !important;
}
.book-content :link, .book-content a {
color: #8db2e5 !important;
}
.book-content img, .book-content img[src] {
z-index: 1;
filter: brightness(0.85) !important;
background-color: initial !important;
}
.book-content *:not(code), .book-content *:not(a) {
background-color: black;
box-shadow: none;
text-shadow: none;
border-radius: unset;
color: #dcdcdc !important;
}
.book-content :visited, .book-content :visited *, .book-content :visited *[class] {color: rgb(211, 138, 138) !important}
.book-content :link:not(cite), :link .book-content *:not(cite) {color: #8db2e5 !important}
.btn-check:checked + .btn {
color: white;
background-color: var(--primary-color);
}
`;

View file

@ -188,19 +188,27 @@ export class CardItemComponent implements OnInit, OnDestroy {
if (this.utilityService.isSeries(this.entity) && updateEvent.seriesId !== this.entity.id) return;
// For volume or Series, we can't just take the event
if (this.utilityService.isChapter(this.entity)) {
const c = this.utilityService.asChapter(this.entity);
c.pagesRead = updateEvent.pagesRead;
this.read = updateEvent.pagesRead;
}
if (this.utilityService.isVolume(this.entity) || this.utilityService.isSeries(this.entity)) {
if (this.utilityService.isVolume(this.entity)) {
const v = this.utilityService.asVolume(this.entity);
const chapter = v.chapters.find(c => c.id === updateEvent.chapterId);
if (chapter) {
let sum = 0;
const chapters = v.chapters.filter(c => c.volumeId === updateEvent.volumeId);
chapters.forEach(chapter => {
chapter.pagesRead = updateEvent.pagesRead;
}
sum += chapter.pagesRead;
});
v.pagesRead = sum;
this.read = sum;
} else {
return;
}
}
this.read = updateEvent.pagesRead;
this.cdRef.detectChanges();
});

View file

@ -45,7 +45,7 @@ export class ConfirmResetPasswordComponent {
submit() {
const model = this.registerForm.getRawValue();
model.token = this.token;
this.accountService.confirmResetPasswordEmail(model).subscribe(() => {
this.accountService.confirmResetPasswordEmail(model).subscribe((response: string) => {
this.toastr.success("Password reset");
this.router.navigateByUrl('login');
}, err => {

View file

@ -640,6 +640,7 @@ export class SeriesDetailComponent implements OnInit, OnDestroy, AfterContentChe
}
openChapter(chapter: Chapter, incognitoMode = false) {
if (this.bulkSelectionService.hasSelections()) return;
if (chapter.pages === 0) {
this.toastr.error('There are no pages. Kavita was not able to read this archive.');
return;
@ -648,6 +649,7 @@ export class SeriesDetailComponent implements OnInit, OnDestroy, AfterContentChe
}
openVolume(volume: Volume) {
if (this.bulkSelectionService.hasSelections()) return;
if (volume.chapters === undefined || volume.chapters?.length === 0) {
this.toastr.error('There are no chapters to this volume. Cannot read.');
return;

View file

@ -53,12 +53,11 @@ export class SideNavComponent implements OnInit, OnDestroy {
ngOnInit(): void {
this.accountService.currentUser$.pipe(take(1)).subscribe(user => {
if (user) {
this.libraryService.getLibraries().pipe(take(1), shareReplay()).subscribe((libraries: Library[]) => {
this.libraries = libraries;
this.cdRef.markForCheck();
});
}
if (!user) return;
this.libraryService.getLibraries().pipe(take(1), shareReplay()).subscribe((libraries: Library[]) => {
this.libraries = libraries;
this.cdRef.markForCheck();
});
this.actions = this.actionFactoryService.getLibraryActions(this.handleAction.bind(this));
this.cdRef.markForCheck();
});

View file

@ -34,7 +34,7 @@ export class RestrictionSelectorComponent implements OnInit, OnChanges {
this.restrictionForm = new FormGroup({
'ageRating': new FormControl(this.member?.ageRestriction.ageRating || AgeRating.NotApplicable || AgeRating.NotApplicable, []),
'ageRestrictionIncludeUnknowns': new FormControl(this.member?.ageRestriction.includeUnknowns, []),
'ageRestrictionIncludeUnknowns': new FormControl(this.member?.ageRestriction.includeUnknowns || false, []),
});

View file

@ -33,4 +33,8 @@ hr {
.subtitle-with-actionables {
margin-left: 32px;
}
.form-switch .form-check-input:checked {
background-color: var(--primary-color);
}