Shakeout Testing Part 2 (#1053)
* Fixed an issue where cover update events wouldn't refresh an image after the second event came through due to randomization * Fixed an issue where download event wasn't send consistently when downloading files. * Fixed a bug where you couldn't add a new role to a user * Fixed a bug where if you went to edit user library, the roles would get reset to nothing * Adjust the rendering on reading list page to be better on smaller screens (and worse on larger ones) * Tweaked the refresh covers message to use queued and not started * Cleaned up the code for image on when to update based on CoverUpdate event. On dashboard, don't spam reload recently added on every series update or scan complete. Debouce for 1 second between calls. * Fixed an issue where we sent an error on forgot password confirmation, but really, it was successful. * Added Reading Lists and Library to search results * Fixed a bug in the search component where hitting esc to close overlay then typing again wouldn't reopen the overlay * When resending invites, send the correct link for an invite * Tell the admin an email was sent on invite * Fixed the error interceptor to flatten validation error messages more robustly and now confirm email will show validation exceptions * Fixed a bug in webtoon reader where we were reading the wrong dimension for fitting images to screen on render * When generating email links, inform who they are for in the logs. Fixed an issue with an error message on login when password was incorrect, but user hadn't confirmed email yet. Fixed multiple cases where migration wasn't sending error messages back correctly and hence the user never saw them. * Show errors on migration UI form * Changed log rolling to be easier to understand * Added some extra logic to throw unauthorized * Tweaked some wording to inform user how to best find email link * Fixed a code smell
This commit is contained in:
parent
d18cfbc44f
commit
4fcab5800e
26 changed files with 211 additions and 65 deletions
|
|
@ -62,9 +62,15 @@ export class ErrorInterceptor implements HttpInterceptor {
|
|||
if (Array.isArray(error.error)) {
|
||||
const modalStateErrors: any[] = [];
|
||||
if (error.error.length > 0 && error.error[0].hasOwnProperty('message')) {
|
||||
error.error.forEach((issue: {status: string, details: string, message: string}) => {
|
||||
modalStateErrors.push(issue.details);
|
||||
});
|
||||
if (error.error[0].details === null) {
|
||||
error.error.forEach((issue: {status: string, details: string, message: string}) => {
|
||||
modalStateErrors.push(issue.message);
|
||||
});
|
||||
} else {
|
||||
error.error.forEach((issue: {status: string, details: string, message: string}) => {
|
||||
modalStateErrors.push(issue.details);
|
||||
});
|
||||
}
|
||||
} else {
|
||||
error.error.forEach((issue: {code: string, description: string}) => {
|
||||
modalStateErrors.push(issue.description);
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
import { Library } from "../library";
|
||||
import { SearchResult } from "../search-result";
|
||||
import { Tag } from "../tag";
|
||||
|
||||
export class SearchResultGroup {
|
||||
libraries: Array<Library> = [];
|
||||
series: Array<SearchResult> = [];
|
||||
collections: Array<Tag> = [];
|
||||
readingLists: Array<Tag> = [];
|
||||
|
|
@ -10,6 +12,7 @@ export class SearchResultGroup {
|
|||
tags: Array<Tag> = [];
|
||||
|
||||
reset() {
|
||||
this.libraries = [];
|
||||
this.series = [];
|
||||
this.collections = [];
|
||||
this.readingLists = [];
|
||||
|
|
|
|||
|
|
@ -149,7 +149,7 @@ export class ActionService implements OnDestroy {
|
|||
}
|
||||
|
||||
this.seriesService.refreshMetadata(series).pipe(take(1)).subscribe((res: any) => {
|
||||
this.toastr.success('Refresh started for ' + series.name);
|
||||
this.toastr.success('Refresh covers queued for ' + series.name);
|
||||
if (callback) {
|
||||
callback(series);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -49,6 +49,18 @@ export class ImageService implements OnDestroy {
|
|||
return this.getChapterCoverImage(item.chapterId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the entity type from a cover image url. Undefied if not applicable
|
||||
* @param url
|
||||
* @returns
|
||||
*/
|
||||
getEntityTypeFromUrl(url: string) {
|
||||
if (url.indexOf('?') < 0) return undefined;
|
||||
const part = url.split('?')[1];
|
||||
const equalIndex = part.indexOf('=');
|
||||
return part.substring(0, equalIndex).replace('Id', '');
|
||||
}
|
||||
|
||||
getVolumeCoverImage(volumeId: number) {
|
||||
return this.baseUrl + 'image/volume-cover?volumeId=' + volumeId;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import { Component, OnInit } from '@angular/core';
|
||||
import { FormControl, FormGroup, Validators } from '@angular/forms';
|
||||
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
|
||||
import { ToastrService } from 'ngx-toastr';
|
||||
import { ConfirmService } from 'src/app/shared/confirm.service';
|
||||
import { Library } from 'src/app/_models/library';
|
||||
import { AccountService } from 'src/app/_services/account.service';
|
||||
|
|
@ -30,7 +31,7 @@ export class InviteUserComponent implements OnInit {
|
|||
public get email() { return this.inviteForm.get('email'); }
|
||||
|
||||
constructor(public modal: NgbActiveModal, private accountService: AccountService, private serverService: ServerService,
|
||||
private confirmService: ConfirmService) { }
|
||||
private confirmService: ConfirmService, private toastr: ToastrService) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
this.inviteForm.addControl('email', new FormControl('', [Validators.required]));
|
||||
|
|
@ -57,10 +58,11 @@ export class InviteUserComponent implements OnInit {
|
|||
libraries: this.selectedLibraries,
|
||||
roles: this.selectedRoles,
|
||||
sendEmail: this.accessible
|
||||
}).subscribe(email => {
|
||||
this.emailLink = email;
|
||||
}).subscribe(emailLink => {
|
||||
this.emailLink = emailLink;
|
||||
this.isSending = false;
|
||||
if (this.accessible) {
|
||||
this.toastr.info('Email sent to ' + email);
|
||||
this.modal.close(true);
|
||||
}
|
||||
}, err => {
|
||||
|
|
|
|||
|
|
@ -34,8 +34,8 @@ export class RoleSelectorComponent implements OnInit {
|
|||
this.selectedRoles = roles.map(item => {
|
||||
return {selected: false, data: item};
|
||||
});
|
||||
this.selected.emit(this.selectedRoles.filter(item => item.selected).map(item => item.data));
|
||||
this.preselect();
|
||||
this.selected.emit(this.selectedRoles.filter(item => item.selected).map(item => item.data));
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -35,6 +35,26 @@
|
|||
</ul>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngIf="readingListTemplate !== undefined && grouppedData.readingLists.length > 0">
|
||||
<li class="list-group-item section-header"><h5>Reading Lists</h5></li>
|
||||
<ul class="list-group results">
|
||||
<li *ngFor="let option of grouppedData.readingLists; let index = index;" (click)="handleResultlick(option)" tabindex="0"
|
||||
class="list-group-item" role="option">
|
||||
<ng-container [ngTemplateOutlet]="readingListTemplate" [ngTemplateOutletContext]="{ $implicit: option, idx: index }"></ng-container>
|
||||
</li>
|
||||
</ul>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngIf="libraryTemplate !== undefined && grouppedData.libraries.length > 0">
|
||||
<li class="list-group-item section-header"><h5 id="libraries-group">Libraries</h5></li>
|
||||
<ul class="list-group results" role="group" aria-describedby="libraries-group">
|
||||
<li *ngFor="let option of grouppedData.libraries; let index = index;" (click)="handleResultlick(option)" tabindex="0"
|
||||
class="list-group-item" aria-labelledby="libraries-group" role="option">
|
||||
<ng-container [ngTemplateOutlet]="libraryTemplate" [ngTemplateOutletContext]="{ $implicit: option, idx: index }"></ng-container>
|
||||
</li>
|
||||
</ul>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngIf="genreTemplate !== undefined && grouppedData.genres.length > 0">
|
||||
<li class="list-group-item section-header"><h5>Genres</h5></li>
|
||||
<ul class="list-group results">
|
||||
|
|
|
|||
|
|
@ -58,6 +58,8 @@ export class GroupedTypeaheadComponent implements OnInit, OnDestroy {
|
|||
@ContentChild('personTemplate') personTemplate: TemplateRef<any> | undefined;
|
||||
@ContentChild('genreTemplate') genreTemplate!: TemplateRef<any>;
|
||||
@ContentChild('noResultsTemplate') noResultsTemplate!: TemplateRef<any>;
|
||||
@ContentChild('libraryTemplate') libraryTemplate!: TemplateRef<any>;
|
||||
@ContentChild('readingListTemplate') readingListTemplate!: TemplateRef<any>;
|
||||
|
||||
|
||||
hasFocus: boolean = false;
|
||||
|
|
@ -103,6 +105,11 @@ export class GroupedTypeaheadComponent implements OnInit, OnDestroy {
|
|||
|
||||
this.typeaheadForm.valueChanges.pipe(debounceTime(this.debounceTime), takeUntil(this.onDestroy)).subscribe(change => {
|
||||
const value = this.typeaheadForm.get('typeahead')?.value;
|
||||
|
||||
if (value != undefined && value != '' && !this.hasFocus) {
|
||||
this.hasFocus = true;
|
||||
}
|
||||
|
||||
if (value != undefined && value.length >= this.minQueryLength) {
|
||||
|
||||
if (this.prevSearchTerm === value) return;
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
</ng-template>
|
||||
</app-carousel-reel>
|
||||
|
||||
<!-- TODO: Refactor this so we can use series actions here -->
|
||||
<app-carousel-reel [items]="recentlyUpdatedSeries" title="Recently Updated Series">
|
||||
<ng-template #carouselItem let-item let-position="idx">
|
||||
<app-card-item [entity]="item" [title]="item.seriesName" [imageUrl]="imageService.getSeriesCoverImage(item.seriesId)"
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import { Component, OnDestroy, OnInit } from '@angular/core';
|
||||
import { Title } from '@angular/platform-browser';
|
||||
import { Router } from '@angular/router';
|
||||
import { Subject } from 'rxjs';
|
||||
import { take, takeUntil } from 'rxjs/operators';
|
||||
import { ReplaySubject, Subject } from 'rxjs';
|
||||
import { debounceTime, take, takeUntil } from 'rxjs/operators';
|
||||
import { RefreshMetadataEvent } from '../_models/events/refresh-metadata-event';
|
||||
import { SeriesAddedEvent } from '../_models/events/series-added-event';
|
||||
import { SeriesRemovedEvent } from '../_models/events/series-removed-event';
|
||||
|
|
@ -36,7 +36,10 @@ export class LibraryComponent implements OnInit, OnDestroy {
|
|||
|
||||
private readonly onDestroy = new Subject<void>();
|
||||
|
||||
seriesTrackBy = (index: number, item: any) => `${item.name}_${item.pagesRead}`;
|
||||
/**
|
||||
* We use this Replay subject to slow the amount of times we reload the UI
|
||||
*/
|
||||
private loadRecentlyAdded$: ReplaySubject<void> = new ReplaySubject<void>();
|
||||
|
||||
constructor(public accountService: AccountService, private libraryService: LibraryService,
|
||||
private seriesService: SeriesService, private router: Router,
|
||||
|
|
@ -49,7 +52,6 @@ export class LibraryComponent implements OnInit, OnDestroy {
|
|||
this.seriesService.getSeries(seriesAddedEvent.seriesId).subscribe(series => {
|
||||
this.recentlyAddedSeries.unshift(series);
|
||||
});
|
||||
this.loadRecentlyAdded();
|
||||
} else if (res.event === EVENTS.SeriesRemoved) {
|
||||
const seriesRemovedEvent = res.payload as SeriesRemovedEvent;
|
||||
|
||||
|
|
@ -59,9 +61,11 @@ export class LibraryComponent implements OnInit, OnDestroy {
|
|||
this.recentlyAddedChapters = this.recentlyAddedChapters.filter(item => item.seriesId != seriesRemovedEvent.seriesId);
|
||||
} else if (res.event === EVENTS.ScanSeries) {
|
||||
// We don't have events for when series are updated, but we do get events when a scan update occurs. Refresh recentlyAdded at that time.
|
||||
this.loadRecentlyAdded();
|
||||
this.loadRecentlyAdded$.next();
|
||||
}
|
||||
});
|
||||
|
||||
this.loadRecentlyAdded$.pipe(debounceTime(1000), takeUntil(this.onDestroy)).subscribe(() => this.loadRecentlyAdded());
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
|
|
|
|||
|
|
@ -143,7 +143,7 @@ export class InfiniteScrollerComponent implements OnInit, OnChanges, OnDestroy {
|
|||
}
|
||||
|
||||
get areImagesWiderThanWindow() {
|
||||
let [innerWidth, _] = this.getInnerDimensions();
|
||||
let [_, innerWidth] = this.getInnerDimensions();
|
||||
return this.webtoonImageWidth > (innerWidth || document.documentElement.clientWidth);
|
||||
}
|
||||
|
||||
|
|
@ -186,6 +186,8 @@ export class InfiniteScrollerComponent implements OnInit, OnChanges, OnDestroy {
|
|||
ngOnInit(): void {
|
||||
this.initScrollHandler();
|
||||
|
||||
this.recalculateImageWidth();
|
||||
|
||||
if (this.goToPage) {
|
||||
this.goToPage.pipe(takeUntil(this.onDestroy)).subscribe(page => {
|
||||
const isSamePage = this.pageNum === page;
|
||||
|
|
@ -218,14 +220,18 @@ export class InfiniteScrollerComponent implements OnInit, OnChanges, OnDestroy {
|
|||
this.fullscreenToggled.pipe(takeUntil(this.onDestroy)).subscribe(isFullscreen => {
|
||||
this.debugLog('[FullScreen] Fullscreen mode: ', isFullscreen);
|
||||
this.isFullscreenMode = isFullscreen;
|
||||
const [innerWidth, _] = this.getInnerDimensions();
|
||||
this.webtoonImageWidth = innerWidth || document.documentElement.clientWidth || document.body.clientWidth;
|
||||
this.recalculateImageWidth();
|
||||
this.initScrollHandler();
|
||||
this.setPageNum(this.pageNum, true);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
recalculateImageWidth() {
|
||||
const [_, innerWidth] = this.getInnerDimensions();
|
||||
this.webtoonImageWidth = innerWidth || document.documentElement.clientWidth || document.body.clientWidth;
|
||||
}
|
||||
|
||||
getVerticalOffset() {
|
||||
const reader = this.isFullscreenMode ? this.readerElemRef.nativeElement : window;
|
||||
|
||||
|
|
@ -338,6 +344,10 @@ export class InfiniteScrollerComponent implements OnInit, OnChanges, OnDestroy {
|
|||
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns Height, Width
|
||||
*/
|
||||
getInnerDimensions() {
|
||||
let innerHeight = window.innerHeight;
|
||||
let innerWidth = window.innerWidth;
|
||||
|
|
@ -399,8 +409,7 @@ export class InfiniteScrollerComponent implements OnInit, OnChanges, OnDestroy {
|
|||
|
||||
initWebtoonReader() {
|
||||
this.initFinished = false;
|
||||
const [innerWidth, _] = this.getInnerDimensions();
|
||||
this.webtoonImageWidth = innerWidth || document.documentElement.clientWidth || document.body.clientWidth;
|
||||
this.recalculateImageWidth();
|
||||
this.imagesLoaded = {};
|
||||
this.webtoonImages.next([]);
|
||||
this.atBottom = false;
|
||||
|
|
|
|||
|
|
@ -72,8 +72,6 @@ export class NavEventsToggleComponent implements OnInit, OnDestroy {
|
|||
|
||||
processProgressEvent(event: Message<ProgressEvent>, eventType: string) {
|
||||
const scanEvent = event.payload as ProgressEvent;
|
||||
console.log(event.event, event.payload);
|
||||
|
||||
|
||||
this.libraryService.getLibraryNames().subscribe(names => {
|
||||
const data = this.progressEventsSource.getValue();
|
||||
|
|
|
|||
|
|
@ -22,6 +22,14 @@
|
|||
(focusChanged)="focusUpdate($event)"
|
||||
>
|
||||
|
||||
<ng-template #libraryTemplate let-item>
|
||||
<div style="display: flex;padding: 5px;" (click)="clickLibraryResult(item)">
|
||||
<div class="ml-1">
|
||||
<span>{{item.name}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</ng-template>
|
||||
|
||||
<ng-template #seriesTemplate let-item>
|
||||
<div style="display: flex;padding: 5px;" (click)="clickSeriesSearchResult(item)">
|
||||
<div style="width: 24px" class="mr-1">
|
||||
|
|
@ -53,6 +61,18 @@
|
|||
</div>
|
||||
</ng-template>
|
||||
|
||||
<ng-template #readingListTemplate let-item>
|
||||
<div style="display: flex;padding: 5px;" (click)="clickReadingListSearchResult(item)">
|
||||
<div class="ml-1">
|
||||
<span>{{item.title}}</span>
|
||||
<span *ngIf="item.promoted">
|
||||
<i class="fa fa-angle-double-up" aria-hidden="true" title="Promoted"></i>
|
||||
<span class="sr-only">(promoted)</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</ng-template>
|
||||
|
||||
<ng-template #tagTemplate let-item>
|
||||
<div style="display: flex;padding: 5px;" (click)="goTo('tags', item.id)">
|
||||
<div class="ml-1">
|
||||
|
|
|
|||
|
|
@ -5,7 +5,9 @@ import { Subject } from 'rxjs';
|
|||
import { takeUntil } from 'rxjs/operators';
|
||||
import { ScrollService } from '../scroll.service';
|
||||
import { CollectionTag } from '../_models/collection-tag';
|
||||
import { Library } from '../_models/library';
|
||||
import { PersonRole } from '../_models/person';
|
||||
import { ReadingList } from '../_models/reading-list';
|
||||
import { SearchResult } from '../_models/search-result';
|
||||
import { SearchResultGroup } from '../_models/search/search-result-group';
|
||||
import { AccountService } from '../_services/account.service';
|
||||
|
|
@ -164,11 +166,20 @@ export class NavHeaderComponent implements OnInit, OnDestroy {
|
|||
this.router.navigate(['library', libraryId, 'series', seriesId]);
|
||||
}
|
||||
|
||||
clickLibraryResult(item: Library) {
|
||||
this.router.navigate(['library', item.id]);
|
||||
}
|
||||
|
||||
clickCollectionSearchResult(item: CollectionTag) {
|
||||
this.clearSearch();
|
||||
this.router.navigate(['collections', item.id]);
|
||||
}
|
||||
|
||||
clickReadingListSearchResult(item: ReadingList) {
|
||||
this.clearSearch();
|
||||
this.router.navigate(['lists', item.id]);
|
||||
}
|
||||
|
||||
|
||||
scrollToTop() {
|
||||
window.scroll({
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
<div class="container-sm mt-2" *ngIf="readingList">
|
||||
<div class="container-fluid mt-2" *ngIf="readingList">
|
||||
<div class="mb-3">
|
||||
<!-- Title row-->
|
||||
<div class="row no-gutters">
|
||||
|
|
|
|||
|
|
@ -8,6 +8,9 @@
|
|||
<p>Your account does not have an email on file. This is a one-time migration. Please add your email to the account. A verficiation link will be sent to your email for you
|
||||
to confirm and will then be allowed to authenticate with this server. This is required.
|
||||
</p>
|
||||
|
||||
<p class="text-danger" *ngIf="error.length > 0">{{error}}</p>
|
||||
|
||||
<form [formGroup]="registerForm">
|
||||
<div class="form-group">
|
||||
<label for="username">Username</label>
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ export class AddEmailToAccountMigrationModalComponent implements OnInit {
|
|||
registerForm: FormGroup = new FormGroup({});
|
||||
emailLink: string = '';
|
||||
emailLinkUrl: SafeUrl | undefined;
|
||||
error: string = '';
|
||||
|
||||
constructor(private accountService: AccountService, private modal: NgbActiveModal,
|
||||
private serverService: ServerService, private confirmService: ConfirmService) {
|
||||
|
|
@ -43,15 +44,18 @@ export class AddEmailToAccountMigrationModalComponent implements OnInit {
|
|||
const model = this.registerForm.getRawValue();
|
||||
model.sendEmail = canAccess;
|
||||
this.accountService.migrateUser(model).subscribe(async (email) => {
|
||||
console.log(email);
|
||||
if (!canAccess) {
|
||||
// Display the email to the user
|
||||
this.emailLink = email;
|
||||
await this.confirmService.alert('Please click this link to confirm your email. You must confirm to be able to login. The link is in your logs. You may need to log out of the current account before clicking. <br/> <a href="' + this.emailLink + '" target="_blank">' + this.emailLink + '</a>');
|
||||
this.modal.close(true);
|
||||
} else {
|
||||
await this.confirmService.alert('Please check your email (or logs) for the confirmation link. You must confirm to be able to login.');
|
||||
await this.confirmService.alert('Please check your email (or logs under "Email Link") for the confirmation link. You must confirm to be able to login.');
|
||||
this.modal.close(true);
|
||||
}
|
||||
}, err => {
|
||||
this.error = err;
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -1,14 +1,14 @@
|
|||
<!--
|
||||
<div class="text-danger" *ngIf="errors.length > 0">
|
||||
<p>Errors:</p>
|
||||
<ul>
|
||||
<li *ngFor="let error of errors">{{error}}</li>
|
||||
</ul>
|
||||
</div> -->
|
||||
|
||||
<app-splash-container>
|
||||
<ng-container title><h2>Register</h2></ng-container>
|
||||
<ng-container body>
|
||||
<p>Complete the form to complete your registration</p>
|
||||
<div class="text-danger" *ngIf="errors.length > 0">
|
||||
<p>Errors:</p>
|
||||
<ul>
|
||||
<li *ngFor="let error of errors">{{error}}</li>
|
||||
</ul>
|
||||
</div>
|
||||
<form [formGroup]="registerForm" (ngSubmit)="submit()">
|
||||
<div class="form-group">
|
||||
<label for="username">Username</label>
|
||||
|
|
|
|||
|
|
@ -53,6 +53,7 @@ export class ConfirmEmailComponent implements OnInit {
|
|||
this.toastr.success('Account registration complete');
|
||||
this.router.navigateByUrl('login');
|
||||
}, err => {
|
||||
console.log('error: ', err);
|
||||
this.errors = err;
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -49,10 +49,16 @@ export class ImageComponent implements OnChanges, OnDestroy {
|
|||
if (res.event === EVENTS.CoverUpdate) {
|
||||
const updateEvent = res.payload as CoverUpdateEvent;
|
||||
if (this.imageUrl === undefined || this.imageUrl === null || this.imageUrl === '') return;
|
||||
const enityType = this.imageService.getEntityTypeFromUrl(this.imageUrl);
|
||||
if (enityType === updateEvent.entityType) {
|
||||
const tokens = this.imageUrl.split('?')[1].split('&random=');
|
||||
|
||||
const tokens = this.imageUrl.split(updateEvent.entityType + 'Id=');
|
||||
if (tokens.length > 1 && tokens[1] === (updateEvent.id + '')) {
|
||||
this.imageUrl = this.imageService.randomize(this.imageUrl);
|
||||
//...seriesId=123&random=
|
||||
const id = tokens[0].replace(enityType + 'Id=', '');
|
||||
if (id === (updateEvent.id + '')) {
|
||||
console.log('Image url: ', this.imageUrl, ' matches update event: ', updateEvent);
|
||||
this.imageUrl = this.imageService.randomize(this.imageUrl);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue