mirror of
https://github.com/Kareadita/Kavita.git
synced 2025-07-31 14:33:50 -04:00
Bugfixes (#1004)
* Refactored the code for choosing the tab to select when updates occur or first load. Specials will no longer be auto-selected if present. We will always try to select Storyline. * Fixed a bug where marking a chapter as unread was actually making it read * When loading a book, put a spinner in the action bar * Fixed an issue with last page not getting marked as read with epubs due to a bugfix from last release * Removed some debug code
This commit is contained in:
parent
6fadbb5231
commit
868eb70506
@ -214,7 +214,7 @@ export class ActionService implements OnDestroy {
|
|||||||
* @param callback Optional callback to perform actions after API completes
|
* @param callback Optional callback to perform actions after API completes
|
||||||
*/
|
*/
|
||||||
markChapterAsUnread(seriesId: number, chapter: Chapter, callback?: ChapterActionCallback) {
|
markChapterAsUnread(seriesId: number, chapter: Chapter, callback?: ChapterActionCallback) {
|
||||||
this.readerService.saveProgress(seriesId, chapter.volumeId, chapter.id, chapter.pages).pipe(take(1)).subscribe(results => {
|
this.readerService.saveProgress(seriesId, chapter.volumeId, chapter.id, 0).pipe(take(1)).subscribe(results => {
|
||||||
chapter.pagesRead = 0;
|
chapter.pagesRead = 0;
|
||||||
this.toastr.success('Marked as unread');
|
this.toastr.success('Marked as unread');
|
||||||
if (callback) {
|
if (callback) {
|
||||||
|
@ -129,18 +129,28 @@
|
|||||||
<button class="btn btn-outline-secondary btn-icon col-2 col-xs-1" (click)="prevPage()"
|
<button class="btn btn-outline-secondary btn-icon col-2 col-xs-1" (click)="prevPage()"
|
||||||
[disabled]="IsPrevDisabled"
|
[disabled]="IsPrevDisabled"
|
||||||
title="{{readingDirection === ReadingDirection.LeftToRight ? 'Previous' : 'Next'}} Page">
|
title="{{readingDirection === ReadingDirection.LeftToRight ? 'Previous' : 'Next'}} Page">
|
||||||
<i class="fa {{(readingDirection === ReadingDirection.LeftToRight ? pageNum === 0 : pageNum + 1 >= maxPages - 1) ? 'fa-angle-double-left' : 'fa-angle-left'}}" aria-hidden="true"></i>
|
<i class="fa {{(readingDirection === ReadingDirection.LeftToRight ? IsPrevChapter : IsNextChapter) ? 'fa-angle-double-left' : 'fa-angle-left'}}" aria-hidden="true"></i>
|
||||||
<span class="phone-hidden"> {{readingDirection === ReadingDirection.LeftToRight ? 'Previous' : 'Next'}}</span>
|
<span class="phone-hidden"> {{readingDirection === ReadingDirection.LeftToRight ? 'Previous' : 'Next'}}</span>
|
||||||
</button>
|
</button>
|
||||||
<button *ngIf="!this.adhocPageHistory.isEmpty()" class="btn btn-outline-secondary btn-icon col-2 col-xs-1" (click)="goBack()" title="Go Back"><i class="fa fa-reply" aria-hidden="true"></i><span class="phone-hidden"> Go Back</span></button>
|
<button *ngIf="!this.adhocPageHistory.isEmpty()" class="btn btn-outline-secondary btn-icon col-2 col-xs-1" (click)="goBack()" title="Go Back"><i class="fa fa-reply" aria-hidden="true"></i><span class="phone-hidden"> Go Back</span></button>
|
||||||
<button class="btn btn-secondary col-2 col-xs-1" (click)="toggleDrawer()"><i class="fa fa-bars" aria-hidden="true"></i><span class="phone-hidden"> Settings</span></button>
|
<button class="btn btn-secondary col-2 col-xs-1" (click)="toggleDrawer()"><i class="fa fa-bars" aria-hidden="true"></i><span class="phone-hidden"> Settings</span></button>
|
||||||
<div class="book-title col-2 phone-hidden">{{bookTitle}} <span *ngIf="incognitoMode" (click)="turnOffIncognito()" role="button" aria-label="Incognito mode is on. Toggle to turn off.">(<i class="fa fa-glasses" aria-hidden="true"></i><span class="sr-only">Incognito Mode</span>)</span></div>
|
<div class="book-title col-2 phone-hidden">
|
||||||
|
<ng-container *ngIf="isLoading; else showTitle">
|
||||||
|
<div class="spinner-border spinner-border-sm text-primary" style="border-radius: 50%;" role="status">
|
||||||
|
<span class="sr-only">Loading book...</span>
|
||||||
|
</div>
|
||||||
|
</ng-container>
|
||||||
|
<ng-template #showTitle>
|
||||||
|
{{bookTitle}}
|
||||||
|
<span *ngIf="incognitoMode" (click)="turnOffIncognito()" role="button" aria-label="Incognito mode is on. Toggle to turn off.">(<i class="fa fa-glasses" aria-hidden="true"></i><span class="sr-only">Incognito Mode</span>)</span>
|
||||||
|
</ng-template>
|
||||||
|
</div>
|
||||||
<button class="btn btn-secondary col-2 col-xs-1" (click)="closeReader()"><i class="fa fa-times-circle" aria-hidden="true"></i><span class="phone-hidden"> Close</span></button>
|
<button class="btn btn-secondary col-2 col-xs-1" (click)="closeReader()"><i class="fa fa-times-circle" aria-hidden="true"></i><span class="phone-hidden"> Close</span></button>
|
||||||
<button class="btn btn-outline-secondary btn-icon col-2 col-xs-1"
|
<button class="btn btn-outline-secondary btn-icon col-2 col-xs-1"
|
||||||
[disabled]="IsNextDisabled"
|
[disabled]="IsNextDisabled"
|
||||||
(click)="nextPage()" title="{{readingDirection === ReadingDirection.LeftToRight ? 'Next' : 'Previous'}} Page">
|
(click)="nextPage()" title="{{readingDirection === ReadingDirection.LeftToRight ? 'Next' : 'Previous'}} Page">
|
||||||
<span class="phone-hidden">{{readingDirection === ReadingDirection.LeftToRight ? 'Next' : 'Previous'}} </span>
|
<span class="phone-hidden">{{readingDirection === ReadingDirection.LeftToRight ? 'Next' : 'Previous'}} </span>
|
||||||
<i class="fa {{(readingDirection === ReadingDirection.LeftToRight ? pageNum + 1 > maxPages - 1 : pageNum === 0) ? 'fa-angle-double-right' : 'fa-angle-right'}}" aria-hidden="true"></i>
|
<i class="fa {{(readingDirection === ReadingDirection.LeftToRight ? IsNextChapter : IsPrevChapter) ? 'fa-angle-double-right' : 'fa-angle-right'}}" aria-hidden="true"></i>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
|
@ -235,6 +235,13 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get IsNextChapter(): boolean {
|
||||||
|
return this.pageNum + 1 >= this.maxPages;
|
||||||
|
}
|
||||||
|
get IsPrevChapter(): boolean {
|
||||||
|
return this.pageNum === 0;
|
||||||
|
}
|
||||||
|
|
||||||
get drawerBackgroundColor() {
|
get drawerBackgroundColor() {
|
||||||
return this.darkMode ? '#010409': '#fff';
|
return this.darkMode ? '#010409': '#fff';
|
||||||
}
|
}
|
||||||
@ -344,12 +351,24 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
this.lastSeenScrollPartPath = path;
|
this.lastSeenScrollPartPath = path;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.lastSeenScrollPartPath !== '' && !this.incognitoMode) {
|
if (this.lastSeenScrollPartPath !== '') {
|
||||||
this.readerService.saveProgress(this.seriesId, this.volumeId, this.chapterId, this.pageNum, this.lastSeenScrollPartPath).pipe(take(1)).subscribe(() => {/* No operation */});
|
this.saveProgress();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
saveProgress() {
|
||||||
|
let tempPageNum = this.pageNum;
|
||||||
|
if (this.pageNum == this.maxPages - 1) {
|
||||||
|
tempPageNum = this.pageNum + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.incognitoMode) {
|
||||||
|
this.readerService.saveProgress(this.seriesId, this.volumeId, this.chapterId, tempPageNum, this.lastSeenScrollPartPath).pipe(take(1)).subscribe(() => {/* No operation */});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
ngOnDestroy(): void {
|
ngOnDestroy(): void {
|
||||||
const bodyNode = this.document.querySelector('body');
|
const bodyNode = this.document.querySelector('body');
|
||||||
if (bodyNode !== undefined && bodyNode !== null && this.originalBodyColor !== undefined) {
|
if (bodyNode !== undefined && bodyNode !== null && this.originalBodyColor !== undefined) {
|
||||||
@ -450,9 +469,7 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
|
|
||||||
if (this.pageNum >= this.maxPages) {
|
if (this.pageNum >= this.maxPages) {
|
||||||
this.pageNum = this.maxPages - 1;
|
this.pageNum = this.maxPages - 1;
|
||||||
if (!this.incognitoMode) {
|
this.saveProgress();
|
||||||
this.readerService.saveProgress(this.seriesId, this.volumeId, this.chapterId, this.pageNum).pipe(take(1)).subscribe(() => {/* No operation */});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.readerService.getNextChapter(this.seriesId, this.volumeId, this.chapterId, this.readingListId).pipe(take(1)).subscribe(chapterId => {
|
this.readerService.getNextChapter(this.seriesId, this.volumeId, this.chapterId, this.readingListId).pipe(take(1)).subscribe(chapterId => {
|
||||||
@ -711,9 +728,7 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
loadPage(part?: string | undefined, scrollTop?: number | undefined) {
|
loadPage(part?: string | undefined, scrollTop?: number | undefined) {
|
||||||
this.isLoading = true;
|
this.isLoading = true;
|
||||||
|
|
||||||
if (!this.incognitoMode) {
|
this.saveProgress();
|
||||||
this.readerService.saveProgress(this.seriesId, this.volumeId, this.chapterId, this.pageNum).pipe(take(1)).subscribe(() => {/* No operation */});
|
|
||||||
}
|
|
||||||
|
|
||||||
this.bookService.getBookPage(this.chapterId, this.pageNum).pipe(take(1)).subscribe(content => {
|
this.bookService.getBookPage(this.chapterId, this.pageNum).pipe(take(1)).subscribe(content => {
|
||||||
this.page = this.domSanitizer.bypassSecurityTrustHtml(content);
|
this.page = this.domSanitizer.bypassSecurityTrustHtml(content);
|
||||||
@ -764,8 +779,8 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
setPageNum(pageNum: number) {
|
setPageNum(pageNum: number) {
|
||||||
if (pageNum < 0) {
|
if (pageNum < 0) {
|
||||||
this.pageNum = 0;
|
this.pageNum = 0;
|
||||||
} else if (pageNum >= this.maxPages) {
|
} else if (pageNum >= this.maxPages - 1) { // This case handles when we are using the pager to move to the next volume/chapter, the pageNum will get incremented past maxPages // NOTE: I made a change where I removed - 1 in comparison, it's breaking page progress
|
||||||
this.pageNum = this.maxPages - 1;
|
this.pageNum = this.maxPages; //
|
||||||
} else {
|
} else {
|
||||||
this.pageNum = pageNum;
|
this.pageNum = pageNum;
|
||||||
}
|
}
|
||||||
@ -794,6 +809,7 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
|
|
||||||
prevPage() {
|
prevPage() {
|
||||||
const oldPageNum = this.pageNum;
|
const oldPageNum = this.pageNum;
|
||||||
|
|
||||||
if (this.readingDirection === ReadingDirection.LeftToRight) {
|
if (this.readingDirection === ReadingDirection.LeftToRight) {
|
||||||
this.setPageNum(this.pageNum - 1);
|
this.setPageNum(this.pageNum - 1);
|
||||||
} else {
|
} else {
|
||||||
@ -816,18 +832,21 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
}
|
}
|
||||||
|
|
||||||
const oldPageNum = this.pageNum;
|
const oldPageNum = this.pageNum;
|
||||||
|
if (oldPageNum + 1 === this.maxPages) {
|
||||||
|
// Move to next volume/chapter automatically
|
||||||
|
this.loadNextChapter();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
if (this.readingDirection === ReadingDirection.LeftToRight) {
|
if (this.readingDirection === ReadingDirection.LeftToRight) {
|
||||||
this.setPageNum(this.pageNum + 1);
|
this.setPageNum(this.pageNum + 1);
|
||||||
} else {
|
} else {
|
||||||
this.setPageNum(this.pageNum - 1);
|
this.setPageNum(this.pageNum - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (oldPageNum + 1 === this.maxPages) {
|
|
||||||
// Move to next volume/chapter automatically
|
|
||||||
this.loadNextChapter();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (oldPageNum === this.pageNum) { return; }
|
if (oldPageNum === this.pageNum) { return; }
|
||||||
|
|
||||||
@ -1051,7 +1070,7 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
const newRoute = this.readerService.getNextChapterUrl(this.router.url, this.chapterId, this.incognitoMode, this.readingListMode, this.readingListId);
|
const newRoute = this.readerService.getNextChapterUrl(this.router.url, this.chapterId, this.incognitoMode, this.readingListMode, this.readingListId);
|
||||||
window.history.replaceState({}, '', newRoute);
|
window.history.replaceState({}, '', newRoute);
|
||||||
this.toastr.info('Incognito mode is off. Progress will now start being tracked.');
|
this.toastr.info('Incognito mode is off. Progress will now start being tracked.');
|
||||||
this.readerService.saveProgress(this.seriesId, this.volumeId, this.chapterId, this.pageNum).pipe(take(1)).subscribe(() => {/* No operation */});
|
this.saveProgress();
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleFullscreen() {
|
toggleFullscreen() {
|
||||||
|
@ -63,7 +63,7 @@
|
|||||||
<div>
|
<div>
|
||||||
<app-bulk-operations [actionCallback]="bulkActionCallback"></app-bulk-operations>
|
<app-bulk-operations [actionCallback]="bulkActionCallback"></app-bulk-operations>
|
||||||
<ul ngbNav #nav="ngbNav" [(activeId)]="activeTabId" class="nav-tabs nav-pills" [destroyOnHide]="false" (navChange)="onNavChange($event)">
|
<ul ngbNav #nav="ngbNav" [(activeId)]="activeTabId" class="nav-tabs nav-pills" [destroyOnHide]="false" (navChange)="onNavChange($event)">
|
||||||
<li [ngbNavItem]="1" *ngIf="hasSpecials">
|
<li [ngbNavItem]="TabID.Specials" *ngIf="hasSpecials">
|
||||||
<a ngbNavLink>Specials</a>
|
<a ngbNavLink>Specials</a>
|
||||||
<ng-template ngbNavContent>
|
<ng-template ngbNavContent>
|
||||||
<div class="row no-gutters">
|
<div class="row no-gutters">
|
||||||
@ -75,7 +75,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
</li>
|
</li>
|
||||||
<li [ngbNavItem]="2" *ngIf="libraryType !== LibraryType.Book && (hasNonSpecialVolumeChapters || hasNonSpecialNonVolumeChapters)">
|
<li [ngbNavItem]="TabID.Storyline" *ngIf="libraryType !== LibraryType.Book && (hasNonSpecialVolumeChapters || hasNonSpecialNonVolumeChapters)">
|
||||||
<a ngbNavLink>Storyline</a>
|
<a ngbNavLink>Storyline</a>
|
||||||
<ng-template ngbNavContent>
|
<ng-template ngbNavContent>
|
||||||
<div class="row no-gutters">
|
<div class="row no-gutters">
|
||||||
@ -92,7 +92,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
</li>
|
</li>
|
||||||
<li [ngbNavItem]="3" *ngIf="hasNonSpecialVolumeChapters">
|
<li [ngbNavItem]="TabID.Volumes" *ngIf="hasNonSpecialVolumeChapters">
|
||||||
<a ngbNavLink>Volumes</a>
|
<a ngbNavLink>Volumes</a>
|
||||||
<ng-template ngbNavContent>
|
<ng-template ngbNavContent>
|
||||||
<div class="row no-gutters">
|
<div class="row no-gutters">
|
||||||
@ -104,7 +104,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
</li>
|
</li>
|
||||||
<li [ngbNavItem]="4" *ngIf="hasNonSpecialNonVolumeChapters">
|
<li [ngbNavItem]="TabID.Chapters" *ngIf="hasNonSpecialNonVolumeChapters">
|
||||||
<a ngbNavLink>{{utilityService.formatChapterName(libraryType) + 's'}}</a>
|
<a ngbNavLink>{{utilityService.formatChapterName(libraryType) + 's'}}</a>
|
||||||
<ng-template ngbNavContent>
|
<ng-template ngbNavContent>
|
||||||
<div class="row no-gutters">
|
<div class="row no-gutters">
|
||||||
|
@ -33,6 +33,13 @@ import { ReaderService } from '../_services/reader.service';
|
|||||||
import { SeriesService } from '../_services/series.service';
|
import { SeriesService } from '../_services/series.service';
|
||||||
|
|
||||||
|
|
||||||
|
enum TabID {
|
||||||
|
Specials = 1,
|
||||||
|
Storyline = 2,
|
||||||
|
Volumes = 3,
|
||||||
|
Chapters = 4
|
||||||
|
}
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-series-detail',
|
selector: 'app-series-detail',
|
||||||
templateUrl: './series-detail.component.html',
|
templateUrl: './series-detail.component.html',
|
||||||
@ -61,7 +68,7 @@ export class SeriesDetailComponent implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
hasSpecials = false;
|
hasSpecials = false;
|
||||||
specials: Array<Chapter> = [];
|
specials: Array<Chapter> = [];
|
||||||
activeTabId = 2;
|
activeTabId = TabID.Storyline;
|
||||||
hasNonSpecialVolumeChapters = false;
|
hasNonSpecialVolumeChapters = false;
|
||||||
hasNonSpecialNonVolumeChapters = false;
|
hasNonSpecialNonVolumeChapters = false;
|
||||||
|
|
||||||
@ -148,6 +155,10 @@ export class SeriesDetailComponent implements OnInit, OnDestroy {
|
|||||||
return TagBadgeCursor;
|
return TagBadgeCursor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get TabID(): typeof TabID {
|
||||||
|
return TabID;
|
||||||
|
}
|
||||||
|
|
||||||
constructor(private route: ActivatedRoute, private seriesService: SeriesService,
|
constructor(private route: ActivatedRoute, private seriesService: SeriesService,
|
||||||
private router: Router, public bulkSelectionService: BulkSelectionService,
|
private router: Router, public bulkSelectionService: BulkSelectionService,
|
||||||
private modalService: NgbModal, public readerService: ReaderService,
|
private modalService: NgbModal, public readerService: ReaderService,
|
||||||
@ -383,31 +394,7 @@ export class SeriesDetailComponent implements OnInit, OnDestroy {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// This shows Chapters/Issues tab
|
this.updateSelectedTab();
|
||||||
// If this has chapters that are not specials
|
|
||||||
if (this.chapters.filter(c => !c.isSpecial).length > 0) {
|
|
||||||
if (this.utilityService.formatChapterName(this.libraryType) == 'Book') {
|
|
||||||
this.activeTabId = 4;
|
|
||||||
}
|
|
||||||
this.hasNonSpecialNonVolumeChapters = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// This shows Volumes tab
|
|
||||||
if (this.volumes.filter(v => v.number !== 0).length !== 0) {
|
|
||||||
if (this.utilityService.formatChapterName(this.libraryType) == 'Book') {
|
|
||||||
this.activeTabId = 3;
|
|
||||||
}
|
|
||||||
this.hasNonSpecialVolumeChapters = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If an update occured and we were on specials, re-activate Volumes/Chapters
|
|
||||||
if (!this.hasSpecials && !this.hasNonSpecialVolumeChapters && this.activeTabId != 2) {
|
|
||||||
this.activeTabId = 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.hasSpecials) {
|
|
||||||
this.activeTabId = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.isLoading = false;
|
this.isLoading = false;
|
||||||
});
|
});
|
||||||
@ -416,6 +403,36 @@ export class SeriesDetailComponent implements OnInit, OnDestroy {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This will update the selected tab
|
||||||
|
*
|
||||||
|
* This assumes loadPage() has already primed all the calculations and state variables. Do not call directly.
|
||||||
|
*/
|
||||||
|
updateSelectedTab() {
|
||||||
|
// This shows Chapters/Issues tab
|
||||||
|
|
||||||
|
// If this has chapters that are not specials
|
||||||
|
if (this.chapters.filter(c => !c.isSpecial).length > 0) {
|
||||||
|
this.hasNonSpecialNonVolumeChapters = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This shows Volumes tab
|
||||||
|
if (this.volumes.filter(v => v.number !== 0).length !== 0) {
|
||||||
|
this.hasNonSpecialVolumeChapters = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If an update occured and we were on specials, re-activate Volumes/Chapters
|
||||||
|
if (!this.hasSpecials && !this.hasNonSpecialVolumeChapters && this.activeTabId != TabID.Storyline) {
|
||||||
|
this.activeTabId = TabID.Storyline;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.hasNonSpecialVolumeChapters || this.hasNonSpecialNonVolumeChapters) {
|
||||||
|
this.activeTabId = TabID.Storyline;
|
||||||
|
} else {
|
||||||
|
this.activeTabId = TabID.Specials;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
createHTML() {
|
createHTML() {
|
||||||
this.userReview = (this.series.userReview === null ? '' : this.series.userReview).replace(/\n/g, '<br>');
|
this.userReview = (this.series.userReview === null ? '' : this.series.userReview).replace(/\n/g, '<br>');
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user