Bugfixes and Cover Chooser Upgrades (#1146)
* Fixed a bug where GetNextChapter would return a loose leaf chapter from a special when it should return nothing. * Fixed a bug in events widget when an update comes in after a user refreshes, the active event counter could get out of sync, thus showing "Nothing going on here" Refactored the events widget to be named appropriately. * Refactored code to have errors during threaded tasks propagate to the UI via events widget (css still needed). Removed ScanLibraryError in favor of generic Error event. * Fixed up some code and added ability to remove the event from events widget * Fixed a bug where modifiying certain fields, like summary, wouldn't lock the field * Fixed a few bugs where lock state was not being set in the DB correctly nor were certain combinations of locking fields and editing fields. * Removed debug code * Updated the discord alert to tag new group * Refactored cover upload to actually handle uploading a temp file via url on the backend so that users can user change cover by url. Fixed up some bugs that occured when chaning the image container in a previous PR. * Code cleanup * Cleaned up the css on the error items * Code cleanup
This commit is contained in:
parent
d2f05cf5ae
commit
e41b455d09
24 changed files with 363 additions and 158 deletions
|
|
@ -32,6 +32,10 @@ export class EditSeriesModalComponent implements OnInit, OnDestroy {
|
|||
@Input() series!: Series;
|
||||
seriesVolumes: any[] = [];
|
||||
isLoadingVolumes = false;
|
||||
/**
|
||||
* A copy of the series from init. This is used to compare values for name fields to see if lock was modified
|
||||
*/
|
||||
initSeries!: Series;
|
||||
|
||||
isCollapsed = true;
|
||||
volumeCollapsed: any = {};
|
||||
|
|
@ -94,6 +98,8 @@ export class EditSeriesModalComponent implements OnInit, OnDestroy {
|
|||
ngOnInit(): void {
|
||||
this.imageUrls.push(this.imageService.getSeriesCoverImage(this.series.id));
|
||||
|
||||
this.initSeries = Object.assign({}, this.series);
|
||||
|
||||
this.libraryService.getLibraryNames().pipe(takeUntil(this.onDestroy)).subscribe(names => {
|
||||
this.libraryName = names[this.series.libraryId];
|
||||
});
|
||||
|
|
@ -133,28 +139,24 @@ export class EditSeriesModalComponent implements OnInit, OnDestroy {
|
|||
this.metadata = metadata;
|
||||
|
||||
this.setupTypeaheads();
|
||||
this.editSeriesForm.get('summary')?.setValue(this.metadata.summary);
|
||||
this.editSeriesForm.get('ageRating')?.setValue(this.metadata.ageRating);
|
||||
this.editSeriesForm.get('publicationStatus')?.setValue(this.metadata.publicationStatus);
|
||||
this.editSeriesForm.get('language')?.setValue(this.metadata.language);
|
||||
this.editSeriesForm.get('summary')?.patchValue(this.metadata.summary);
|
||||
this.editSeriesForm.get('ageRating')?.patchValue(this.metadata.ageRating);
|
||||
this.editSeriesForm.get('publicationStatus')?.patchValue(this.metadata.publicationStatus);
|
||||
this.editSeriesForm.get('language')?.patchValue(this.metadata.language);
|
||||
|
||||
this.editSeriesForm.get('name')?.valueChanges.pipe(takeUntil(this.onDestroy)).subscribe(val => {
|
||||
if (!this.editSeriesForm.get('name')?.touched) return;
|
||||
this.series.nameLocked = true;
|
||||
});
|
||||
|
||||
this.editSeriesForm.get('sortName')?.valueChanges.pipe(takeUntil(this.onDestroy)).subscribe(val => {
|
||||
if (!this.editSeriesForm.get('sortName')?.touched) return;
|
||||
this.series.sortNameLocked = true;
|
||||
});
|
||||
|
||||
this.editSeriesForm.get('localizedName')?.valueChanges.pipe(takeUntil(this.onDestroy)).subscribe(val => {
|
||||
if (!this.editSeriesForm.get('localizedName')?.touched) return;
|
||||
this.series.localizedNameLocked = true;
|
||||
});
|
||||
|
||||
this.editSeriesForm.get('summary')?.valueChanges.pipe(takeUntil(this.onDestroy)).subscribe(val => {
|
||||
if (!this.editSeriesForm.get('summary')?.touched) return;
|
||||
this.metadata.summaryLocked = true;
|
||||
this.metadata.summary = val;
|
||||
});
|
||||
|
|
@ -203,7 +205,6 @@ export class EditSeriesModalComponent implements OnInit, OnDestroy {
|
|||
this.setupLanguageTypeahead()
|
||||
]).subscribe(results => {
|
||||
this.collectionTags = this.metadata.collectionTags;
|
||||
this.editSeriesForm.get('summary')?.setValue(this.metadata.summary);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -345,7 +346,6 @@ export class EditSeriesModalComponent implements OnInit, OnDestroy {
|
|||
this.updateFromPreset('publisher', this.metadata.publishers, PersonRole.Publisher),
|
||||
this.updateFromPreset('translator', this.metadata.translators, PersonRole.Translator)
|
||||
]).pipe(map(results => {
|
||||
//this.resetTypeaheads.next(true);
|
||||
return of(true);
|
||||
}));
|
||||
}
|
||||
|
|
@ -406,7 +406,12 @@ export class EditSeriesModalComponent implements OnInit, OnDestroy {
|
|||
];
|
||||
|
||||
// We only need to call updateSeries if we changed name, sort name, or localized name or reset a cover image
|
||||
if (this.editSeriesForm.get('name')?.dirty || this.editSeriesForm.get('sortName')?.dirty || this.editSeriesForm.get('localizedName')?.dirty || this.coverImageReset) {
|
||||
const nameFieldsDirty = this.editSeriesForm.get('name')?.dirty || this.editSeriesForm.get('sortName')?.dirty || this.editSeriesForm.get('localizedName')?.dirty;
|
||||
const nameFieldLockChanged = this.series.nameLocked !== this.initSeries.nameLocked || this.series.sortNameLocked !== this.initSeries.sortNameLocked || this.series.localizedNameLocked !== this.initSeries.localizedNameLocked;
|
||||
if (nameFieldsDirty || nameFieldLockChanged || this.coverImageReset) {
|
||||
model.nameLocked = this.series.nameLocked;
|
||||
model.sortNameLocked = this.series.sortNameLocked;
|
||||
model.localizedNameLocked = this.series.localizedNameLocked;
|
||||
apis.push(this.seriesService.updateSeries(model));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import { takeWhile } from 'rxjs/operators';
|
|||
import { ToastrService } from 'ngx-toastr';
|
||||
import { ImageService } from 'src/app/_services/image.service';
|
||||
import { KEY_CODES } from 'src/app/shared/_services/utility.service';
|
||||
import { UploadService } from 'src/app/_services/upload.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-cover-image-chooser',
|
||||
|
|
@ -41,7 +42,7 @@ export class CoverImageChooserComponent implements OnInit, OnDestroy {
|
|||
mode: 'file' | 'url' | 'all' = 'all';
|
||||
private readonly onDestroy = new Subject<void>();
|
||||
|
||||
constructor(public imageService: ImageService, private fb: FormBuilder, private toastr: ToastrService) { }
|
||||
constructor(public imageService: ImageService, private fb: FormBuilder, private toastr: ToastrService, private uploadService: UploadService) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
this.form = this.fb.group({
|
||||
|
|
@ -72,49 +73,31 @@ export class CoverImageChooserComponent implements OnInit, OnDestroy {
|
|||
if (this.selectedIndex === index) { return; }
|
||||
this.selectedIndex = index;
|
||||
this.imageSelected.emit(this.selectedIndex);
|
||||
const selector = `.chooser img[src="${this.imageUrls[this.selectedIndex]}"]`;
|
||||
|
||||
|
||||
const elem = document.querySelector(selector) || document.querySelectorAll('.chooser img.card-img-top')[this.selectedIndex];
|
||||
if (elem) {
|
||||
const imageElem = <HTMLImageElement>elem;
|
||||
if (imageElem.src.startsWith('data')) {
|
||||
this.selectedBase64Url.emit(imageElem.src);
|
||||
return;
|
||||
}
|
||||
const image = this.getBase64Image(imageElem);
|
||||
if (image != '') {
|
||||
this.selectedBase64Url.emit(image);
|
||||
}
|
||||
}
|
||||
this.selectedBase64Url.emit(this.imageUrls[this.selectedIndex]);
|
||||
}
|
||||
|
||||
loadImage() {
|
||||
const url = this.form.get('coverImageUrl')?.value.trim();
|
||||
if (url && url != '') {
|
||||
const img = new Image();
|
||||
img.crossOrigin = 'Anonymous';
|
||||
img.onload = (e) => this.handleUrlImageAdd(e);
|
||||
img.onerror = (e) => {
|
||||
this.toastr.error('The image could not be fetched due to server refusing request. Please download and upload from file instead.');
|
||||
this.form.get('coverImageUrl')?.setValue('');
|
||||
};
|
||||
img.src = this.form.get('coverImageUrl')?.value;
|
||||
this.form.get('coverImageUrl')?.setValue('');
|
||||
|
||||
this.uploadService.uploadByUrl(url).subscribe(filename => {
|
||||
const img = new Image();
|
||||
img.crossOrigin = 'Anonymous';
|
||||
img.src = this.imageService.getCoverUploadImage(filename);
|
||||
img.onload = (e) => this.handleUrlImageAdd(e);
|
||||
img.onerror = (e) => {
|
||||
this.toastr.error('The image could not be fetched due to server refusing request. Please download and upload from file instead.');
|
||||
this.form.get('coverImageUrl')?.setValue('');
|
||||
};
|
||||
this.form.get('coverImageUrl')?.setValue('');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
changeMode(mode: 'url') {
|
||||
this.mode = mode;
|
||||
this.setupEnterHandler();
|
||||
setTimeout(() => {
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public dropped(files: NgxFileDropEntry[]) {
|
||||
this.files = files;
|
||||
|
|
@ -151,7 +134,7 @@ export class CoverImageChooserComponent implements OnInit, OnDestroy {
|
|||
|
||||
setTimeout(() => {
|
||||
// Auto select newly uploaded image and tell parent of new base64 url
|
||||
this.selectImage(this.selectedIndex + 1)
|
||||
this.selectImage(this.selectedIndex + 1);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue