Add pdf webworker and progress bar
Generate PDF in the background using webworker Shows a progress bar to estimate the PDF generation progress
This commit is contained in:
parent
2f7937a27d
commit
6123216afc
@ -31,7 +31,8 @@
|
||||
{ "glob": "**/*", "input": "node_modules/tinymce/plugins", "output": "/tinymce/plugins/" }
|
||||
],
|
||||
"styles": ["src/styles.scss"],
|
||||
"scripts": ["node_modules/tinymce/tinymce.min.js"]
|
||||
"scripts": ["node_modules/tinymce/tinymce.min.js"],
|
||||
"webWorkerTsConfig": "tsconfig.worker.json"
|
||||
},
|
||||
"configurations": {
|
||||
"production": {
|
||||
|
@ -2,7 +2,7 @@ import { inject, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { E2EImportsModule } from 'e2e-imports.module';
|
||||
|
||||
import { PdfDocumentService } from '../ui-services/pdf-document.service';
|
||||
import { PdfDocumentService } from './pdf-document.service';
|
||||
|
||||
describe('PdfDocumentService', () => {
|
||||
beforeEach(() => {
|
@ -1,13 +1,15 @@
|
||||
import { HttpHeaders } from '@angular/common/http';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { MatSnackBar } from '@angular/material';
|
||||
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { saveAs } from 'file-saver';
|
||||
import pdfMake from 'pdfmake/build/pdfmake';
|
||||
|
||||
import { ProgressSnackBarComponent } from 'app/shared/components/progress-snack-bar/progress-snack-bar.component';
|
||||
import { ExportFormData } from 'app/site/motions/modules/motion-list/components/motion-export-dialog/motion-export-dialog.component';
|
||||
import { ConfigService } from './config.service';
|
||||
import { ConfigService } from '../ui-services/config.service';
|
||||
import { HttpService } from '../core-services/http.service';
|
||||
import { ProgressService } from '../ui-services/progress.service';
|
||||
|
||||
/**
|
||||
* Enumeration to define possible values for the styling.
|
||||
@ -51,7 +53,7 @@ export class PdfError extends Error {
|
||||
* @example
|
||||
* ```ts
|
||||
* const motionContent = this.motionPdfService.motionToDocDef(this.motion);
|
||||
* this.this.pdfDocumentService.open(motionContent);
|
||||
* this.pdfDocumentService.download(motionContent, 'test.pdf');
|
||||
* ```
|
||||
*/
|
||||
@Injectable({
|
||||
@ -64,25 +66,6 @@ export class PdfDocumentService {
|
||||
*/
|
||||
private imageUrls: string[] = [];
|
||||
|
||||
/**
|
||||
* Table layout that switches the background color every other row.
|
||||
* @example
|
||||
* ```ts
|
||||
* layout: this.pdfDocumentService.switchColorTableLayout
|
||||
* ```
|
||||
*/
|
||||
public switchColorTableLayout = {
|
||||
hLineWidth: rowIndex => {
|
||||
return rowIndex === 1;
|
||||
},
|
||||
vLineWidth: () => {
|
||||
return 0;
|
||||
},
|
||||
fillColor: rowIndex => {
|
||||
return rowIndex % 2 === 0 ? '#EEEEEE' : null;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
@ -93,7 +76,9 @@ export class PdfDocumentService {
|
||||
public constructor(
|
||||
private translate: TranslateService,
|
||||
private configService: ConfigService,
|
||||
private httpService: HttpService
|
||||
private httpService: HttpService,
|
||||
private matSnackBar: MatSnackBar,
|
||||
private progressService: ProgressService
|
||||
) {}
|
||||
|
||||
/**
|
||||
@ -183,13 +168,9 @@ export class PdfDocumentService {
|
||||
customMargins?: [number, number, number, number],
|
||||
landscape?: boolean
|
||||
): Promise<object> {
|
||||
this.initFonts();
|
||||
this.imageUrls = imageUrls ? imageUrls : [];
|
||||
pdfMake.vfs = await this.initVfs();
|
||||
const pageSize = this.configService.instant('general_export_pdf_pagesize');
|
||||
const defaultMargins = pageSize === 'A5' ? [45, 30, 45, 45] : [75, 90, 75, 75];
|
||||
// needs to be done before, cause the footer is async
|
||||
this.loadFooterImages();
|
||||
const result = {
|
||||
pageSize: pageSize || 'A4',
|
||||
pageOrientation: landscape ? 'landscape' : 'portrait',
|
||||
@ -199,21 +180,12 @@ export class PdfDocumentService {
|
||||
fontSize: this.configService.instant('general_export_pdf_fontsize')
|
||||
},
|
||||
header: this.getHeader(customMargins ? [customMargins[0], customMargins[2]] : null),
|
||||
|
||||
// TODO: option for no footer, wherever this can be defined
|
||||
footer: (currentPage, pageCount) => {
|
||||
return this.getFooter(
|
||||
currentPage,
|
||||
pageCount,
|
||||
customMargins ? [customMargins[0], customMargins[2]] : null,
|
||||
exportInfo
|
||||
);
|
||||
},
|
||||
// real footer gets created in the worker
|
||||
tmpfooter: this.getFooter(customMargins ? [customMargins[0], customMargins[2]] : null, exportInfo),
|
||||
info: metadata,
|
||||
content: documentContent,
|
||||
styles: this.getStandardPaperStyles()
|
||||
};
|
||||
await this.loadAllImages();
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -226,9 +198,7 @@ export class PdfDocumentService {
|
||||
* @returns the pdf document definition ready to export
|
||||
*/
|
||||
private async getBallotPaper(documentContent: object, imageUrl?: string): Promise<object> {
|
||||
this.initFonts();
|
||||
this.imageUrls = imageUrl ? [imageUrl] : [];
|
||||
pdfMake.vfs = await this.initVfs();
|
||||
const result = {
|
||||
pageSize: 'A4',
|
||||
pageMargins: [0, 0, 0, 0],
|
||||
@ -239,21 +209,18 @@ export class PdfDocumentService {
|
||||
content: documentContent,
|
||||
styles: this.getBlankPaperStyles()
|
||||
};
|
||||
await this.loadAllImages();
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the fonts
|
||||
* Get pdfFonts from storage
|
||||
*/
|
||||
private initFonts(): void {
|
||||
pdfMake.fonts = {
|
||||
PdfFont: {
|
||||
normal: this.getFontName('font_regular'),
|
||||
bold: this.getFontName('font_bold'),
|
||||
italics: this.getFontName('font_italic'),
|
||||
bolditalics: this.getFontName('font_bold_italic')
|
||||
}
|
||||
private getPdfFonts(): object {
|
||||
return {
|
||||
normal: this.getFontName('font_regular'),
|
||||
bold: this.getFontName('font_bold'),
|
||||
italics: this.getFontName('font_italic'),
|
||||
bolditalics: this.getFontName('font_bold_italic')
|
||||
};
|
||||
}
|
||||
|
||||
@ -341,14 +308,9 @@ export class PdfDocumentService {
|
||||
* @param lrMargin optionally overriding the margins
|
||||
* @returns the footer doc definition
|
||||
*/
|
||||
private getFooter(
|
||||
currentPage: number,
|
||||
pageCount: number,
|
||||
lrMargin?: [number, number],
|
||||
exportInfo?: ExportFormData
|
||||
): object {
|
||||
private getFooter(lrMargin?: [number, number], exportInfo?: ExportFormData): object {
|
||||
const columns = [];
|
||||
const showPage = exportInfo && exportInfo.pdfOptions ? exportInfo.pdfOptions.includes('page') : true;
|
||||
const showPageNr = exportInfo && exportInfo.pdfOptions ? exportInfo.pdfOptions.includes('page') : true;
|
||||
const showDate = exportInfo && exportInfo.pdfOptions ? exportInfo.pdfOptions.includes('date') : false;
|
||||
let logoContainerWidth: string;
|
||||
let pageNumberPosition: string;
|
||||
@ -357,11 +319,10 @@ export class PdfDocumentService {
|
||||
const logoFooterRightUrl = this.configService.instant<any>('logo_pdf_footer_R').path;
|
||||
|
||||
let footerPageNumber = '';
|
||||
if (showPage) {
|
||||
footerPageNumber += `${currentPage} / ${pageCount}`;
|
||||
if (showDate) {
|
||||
footerPageNumber += '\n';
|
||||
}
|
||||
if (showPageNr) {
|
||||
// footerPageNumber += `${currentPage} / ${pageCount}`;
|
||||
// replace with `${currentPage} / ${pageCount}` in worker
|
||||
footerPageNumber += `%PAGENR%`;
|
||||
}
|
||||
|
||||
let footerDate = {};
|
||||
@ -402,11 +363,12 @@ export class PdfDocumentService {
|
||||
width: logoContainerWidth,
|
||||
alignment: 'left'
|
||||
});
|
||||
this.imageUrls.push(logoFooterLeftUrl);
|
||||
}
|
||||
|
||||
// add the page number
|
||||
columns.push({
|
||||
text: [footerPageNumber, footerDate],
|
||||
stack: [footerPageNumber, footerDate],
|
||||
style: 'footerPageNumber',
|
||||
alignment: pageNumberPosition
|
||||
});
|
||||
@ -419,6 +381,7 @@ export class PdfDocumentService {
|
||||
width: logoContainerWidth,
|
||||
alignment: 'right'
|
||||
});
|
||||
this.imageUrls.push(logoFooterRightUrl);
|
||||
}
|
||||
|
||||
const margin = [lrMargin ? lrMargin[0] : 75, 0, lrMargin ? lrMargin[0] : 75, 10];
|
||||
@ -454,6 +417,7 @@ export class PdfDocumentService {
|
||||
this.createPdf(doc, filename);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Downloads a pdf with the ballot papet page definitions.
|
||||
*
|
||||
@ -471,12 +435,57 @@ export class PdfDocumentService {
|
||||
* Triggers the actual page creation and saving.
|
||||
*
|
||||
* @param doc the finished layout
|
||||
* @param filename the filename (without extension) to save as
|
||||
* @param filetitle the filename (without extension) to save as
|
||||
*/
|
||||
private createPdf(doc: object, filename: string): void {
|
||||
pdfMake.createPdf(doc).getBlob(blob => {
|
||||
saveAs(blob, `${filename}.pdf`, { autoBOM: true });
|
||||
private async createPdf(doc: object, filetitle: string): Promise<void> {
|
||||
const filename = `${filetitle}.pdf`;
|
||||
|
||||
// set the required progress info
|
||||
this.progressService.progressInfo = {
|
||||
mode: 'determinate',
|
||||
text: filename
|
||||
};
|
||||
|
||||
// open progress bar
|
||||
this.matSnackBar.openFromComponent(ProgressSnackBarComponent, {
|
||||
duration: 0
|
||||
});
|
||||
const fonts = this.getPdfFonts();
|
||||
const vfs = await this.initVfs();
|
||||
await this.loadAllImages(vfs);
|
||||
|
||||
if (typeof Worker !== 'undefined') {
|
||||
const worker = new Worker('./pdf-worker.worker', {
|
||||
type: 'module'
|
||||
});
|
||||
|
||||
// the result of the worker
|
||||
worker.onmessage = ({ data }) => {
|
||||
// if the worker returns a numbers, is always the progress
|
||||
if (typeof data === 'number') {
|
||||
// update progress
|
||||
const progress = Math.ceil(data * 100);
|
||||
this.progressService.progressAmount = progress;
|
||||
}
|
||||
|
||||
// if the worker returns an object, it's always the document
|
||||
if (typeof data === 'object') {
|
||||
// close progress bar
|
||||
this.matSnackBar.dismiss();
|
||||
saveAs(data, filename, { autoBOM: true });
|
||||
}
|
||||
};
|
||||
|
||||
worker.postMessage({
|
||||
doc: JSON.parse(JSON.stringify(doc)),
|
||||
fonts: fonts,
|
||||
vfs: vfs
|
||||
});
|
||||
} else {
|
||||
this.matSnackBar.open(this.translate.instant('Web workers are not supported on your browser.'), '', {
|
||||
duration: 0
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -612,30 +621,13 @@ export class PdfDocumentService {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the footer images to the imageUrls-list, cause the create footer function is async and
|
||||
* potentially called after loadAllImages was called.
|
||||
*/
|
||||
private loadFooterImages(): void {
|
||||
const logoFooterLeftUrl = this.configService.instant<any>('logo_pdf_footer_L').path;
|
||||
const logoFooterRightUrl = this.configService.instant<any>('logo_pdf_footer_R').path;
|
||||
|
||||
if (logoFooterLeftUrl) {
|
||||
this.imageUrls.push(logoFooterLeftUrl);
|
||||
}
|
||||
|
||||
if (logoFooterRightUrl) {
|
||||
this.imageUrls.push(logoFooterRightUrl);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Triggers the addition of all images found during creation(including header and footer)
|
||||
* to the vfs.
|
||||
*/
|
||||
private async loadAllImages(): Promise<void> {
|
||||
private async loadAllImages(vfs: object): Promise<void> {
|
||||
const promises = this.imageUrls.map(image => {
|
||||
return this.addImageToVfS(image);
|
||||
return this.addImageToVfS(image, vfs);
|
||||
});
|
||||
await Promise.all(promises);
|
||||
}
|
||||
@ -645,14 +637,14 @@ export class PdfDocumentService {
|
||||
*
|
||||
* @param url
|
||||
*/
|
||||
private async addImageToVfS(url: string): Promise<void> {
|
||||
private async addImageToVfS(url: string, vfs: object): Promise<void> {
|
||||
if (url.indexOf('/') === 0) {
|
||||
url = url.substr(1);
|
||||
}
|
||||
|
||||
if (!pdfMake.vfs[url]) {
|
||||
if (!vfs[url]) {
|
||||
const base64 = await this.convertUrlToBase64(url);
|
||||
pdfMake.vfs[url] = base64;
|
||||
vfs[url] = base64;
|
||||
}
|
||||
}
|
||||
|
114
client/src/app/core/pdf-services/pdf-worker.worker.ts
Normal file
114
client/src/app/core/pdf-services/pdf-worker.worker.ts
Normal file
@ -0,0 +1,114 @@
|
||||
/// <reference lib="webworker" />
|
||||
|
||||
import pdfMake from 'pdfmake/build/pdfmake';
|
||||
|
||||
const osTableLayout = {
|
||||
switchColorTableLayout: {
|
||||
hLineWidth: rowIndex => {
|
||||
return rowIndex === 1;
|
||||
},
|
||||
vLineWidth: () => {
|
||||
return 0;
|
||||
},
|
||||
fillColor: rowIndex => {
|
||||
return rowIndex % 2 === 0 ? '#EEEEEE' : null;
|
||||
}
|
||||
},
|
||||
metaboxLayout: {
|
||||
fillColor: () => {
|
||||
return '#dddddd';
|
||||
},
|
||||
hLineWidth: (i, node) => {
|
||||
return i === 0 || i === node.table.body.length ? 0 : 0.5;
|
||||
},
|
||||
vLineWidth: () => {
|
||||
return 0;
|
||||
},
|
||||
hLineColor: () => {
|
||||
return 'white';
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function applyLayout(content: any): void {
|
||||
for (const section of content) {
|
||||
if (Array.isArray(section)) {
|
||||
applyLayout(section);
|
||||
} else {
|
||||
if (!!section.layout) {
|
||||
let layout: object;
|
||||
switch (section.layout) {
|
||||
case 'switchColorTableLayout': {
|
||||
layout = osTableLayout.switchColorTableLayout;
|
||||
break;
|
||||
}
|
||||
case 'metaboxLayout': {
|
||||
layout = osTableLayout.metaboxLayout;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!!layout) {
|
||||
section.layout = layout;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the internal PdfMake fonts and VFS
|
||||
*/
|
||||
function initPdfMake(data: any): void {
|
||||
pdfMake.fonts = {
|
||||
PdfFont: data.fonts
|
||||
};
|
||||
|
||||
pdfMake.vfs = data.vfs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace the palceholder with actual page numbers
|
||||
* @param data
|
||||
*/
|
||||
function addPageNumbers(data: any): void {
|
||||
// to allow page numbers in every page, after the initial "%PAGENR%" placeholder was reset
|
||||
let countPageNumbers = false;
|
||||
|
||||
data.doc.footer = (currentPage, pageCount) => {
|
||||
const footer = data.doc.tmpfooter;
|
||||
// "%PAGENR% needs to be found once. After that, the same position should always update page numbers"
|
||||
if (footer.columns[0].stack[0] === '%PAGENR%' || countPageNumbers) {
|
||||
countPageNumbers = true;
|
||||
footer.columns[0].stack[0] = `${currentPage} / ${pageCount}`;
|
||||
}
|
||||
return footer;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* The actual web worker code
|
||||
*/
|
||||
addEventListener('message', ({ data }) => {
|
||||
initPdfMake(data);
|
||||
|
||||
applyLayout(data.doc.content);
|
||||
|
||||
if (!!data.doc.tmpfooter) {
|
||||
addPageNumbers(data);
|
||||
}
|
||||
|
||||
const pdfGenerator = pdfMake.createPdf(data.doc);
|
||||
|
||||
pdfGenerator.getBlob(
|
||||
blob => {
|
||||
// post the result back to the main thread
|
||||
postMessage(blob);
|
||||
},
|
||||
{
|
||||
progressCallback: progress => {
|
||||
postMessage(progress);
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
12
client/src/app/core/ui-services/progress.service.spec.ts
Normal file
12
client/src/app/core/ui-services/progress.service.spec.ts
Normal file
@ -0,0 +1,12 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
|
||||
import { ProgressService } from './progress.service';
|
||||
|
||||
describe('ProgressService', () => {
|
||||
beforeEach(() => TestBed.configureTestingModule({}));
|
||||
|
||||
it('should be created', () => {
|
||||
const service: ProgressService = TestBed.get(ProgressService);
|
||||
expect(service).toBeTruthy();
|
||||
});
|
||||
});
|
63
client/src/app/core/ui-services/progress.service.ts
Normal file
63
client/src/app/core/ui-services/progress.service.ts
Normal file
@ -0,0 +1,63 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
|
||||
import { Subject } from 'rxjs';
|
||||
|
||||
/**
|
||||
* Determine the progress mode
|
||||
*/
|
||||
export type ProgressMode = 'determinate' | 'indeterminate' | 'buffer' | 'query';
|
||||
|
||||
/**
|
||||
* Shape of the progress info
|
||||
*/
|
||||
export interface ProgressInfo {
|
||||
mode: ProgressMode;
|
||||
text?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper service to announce some sort of progress, determinate or indeterminate.
|
||||
*/
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class ProgressService {
|
||||
/**
|
||||
* Subject to get progress information
|
||||
*/
|
||||
private _progressInfo: Subject<ProgressInfo> = new Subject();
|
||||
|
||||
/**
|
||||
* Subject to get the progress amount
|
||||
*/
|
||||
private _progressAmount: Subject<number> = new Subject();
|
||||
|
||||
/**
|
||||
* Get the progress information as observable
|
||||
*/
|
||||
public get info(): Subject<ProgressInfo> {
|
||||
return this._progressInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the progres amount as observable
|
||||
*/
|
||||
public get amount(): Subject<number> {
|
||||
return this._progressAmount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the progress info. Usually only required once for every part if new information
|
||||
*/
|
||||
public set progressInfo(newInfo: ProgressInfo) {
|
||||
setTimeout(() => this._progressInfo.next(newInfo));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the new progress amount. Can be called whenever new info about the progress
|
||||
* is available. Required only if the ProgressMode is set to 'determinate'
|
||||
*/
|
||||
public set progressAmount(newAmount: number) {
|
||||
this._progressAmount.next(newAmount);
|
||||
}
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
<div>
|
||||
{{ message | translate }}
|
||||
</div>
|
||||
<div>
|
||||
<mat-progress-bar color="accent" [mode]="mode" [value]="value"></mat-progress-bar>
|
||||
</div>
|
@ -0,0 +1,26 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { E2EImportsModule } from 'e2e-imports.module';
|
||||
|
||||
import { ProgressSnackBarComponent } from './progress-snack-bar.component';
|
||||
|
||||
describe('ProgressSnackBarComponent', () => {
|
||||
let component: ProgressSnackBarComponent;
|
||||
let fixture: ComponentFixture<ProgressSnackBarComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [E2EImportsModule]
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(ProgressSnackBarComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
@ -0,0 +1,110 @@
|
||||
import {
|
||||
ChangeDetectionStrategy,
|
||||
ChangeDetectorRef,
|
||||
Component,
|
||||
OnDestroy,
|
||||
OnInit,
|
||||
ViewEncapsulation
|
||||
} from '@angular/core';
|
||||
|
||||
import { Subscription } from 'rxjs';
|
||||
import { distinctUntilChanged } from 'rxjs/operators';
|
||||
|
||||
import { ProgressMode, ProgressService } from 'app/core/ui-services/progress.service';
|
||||
|
||||
/**
|
||||
* Component to show the progress announced in the progress service in a snack bar
|
||||
* component
|
||||
*/
|
||||
@Component({
|
||||
selector: 'os-progress-snack-bar',
|
||||
templateUrl: './progress-snack-bar.component.html',
|
||||
styleUrls: ['./progress-snack-bar.component.scss'],
|
||||
encapsulation: ViewEncapsulation.None,
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class ProgressSnackBarComponent implements OnInit, OnDestroy {
|
||||
/**
|
||||
* Private declaration of the mode
|
||||
*/
|
||||
private _mode: ProgressMode = 'indeterminate';
|
||||
|
||||
/**
|
||||
* Private declaration of the progress message
|
||||
*/
|
||||
private _message = '';
|
||||
|
||||
/**
|
||||
* Private declaration of the value
|
||||
*/
|
||||
private _value = 0;
|
||||
|
||||
/**
|
||||
* The sub for the info
|
||||
*/
|
||||
private infoSubscription: Subscription;
|
||||
|
||||
/**
|
||||
* The sub for the amount
|
||||
*/
|
||||
private valueSubscription: Subscription;
|
||||
|
||||
/**
|
||||
* Public getter of the progress bar mode
|
||||
*/
|
||||
public get mode(): ProgressMode {
|
||||
return this._mode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Public getter of the progress bar message. Will be trasnslated in the UIs
|
||||
*/
|
||||
public get message(): string {
|
||||
return this._message;
|
||||
}
|
||||
|
||||
/**
|
||||
* Public getter for the progress value
|
||||
*/
|
||||
public get value(): number {
|
||||
return this._value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Declare the progressService
|
||||
*/
|
||||
public constructor(private progressService: ProgressService, private cd: ChangeDetectorRef) {}
|
||||
|
||||
/**
|
||||
* Get the progress subject and subscribe to the info subject
|
||||
*/
|
||||
public ngOnInit(): void {
|
||||
this.infoSubscription = this.progressService.info.subscribe(info => {
|
||||
this._message = info.text;
|
||||
this._mode = info.mode;
|
||||
this.cd.detectChanges();
|
||||
});
|
||||
|
||||
this.valueSubscription = this.progressService.amount.pipe(distinctUntilChanged()).subscribe(value => {
|
||||
if (value - this._value >= 5 || value === 100) {
|
||||
this._value = value;
|
||||
this.cd.detectChanges();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* clear the Subscriptions
|
||||
*/
|
||||
public ngOnDestroy(): void {
|
||||
if (this.infoSubscription) {
|
||||
this.infoSubscription.unsubscribe();
|
||||
this.infoSubscription = null;
|
||||
}
|
||||
|
||||
if (this.valueSubscription) {
|
||||
this.valueSubscription.unsubscribe();
|
||||
this.valueSubscription = null;
|
||||
}
|
||||
}
|
||||
}
|
@ -96,6 +96,7 @@ import { AgendaContentObjectFormComponent } from './components/agenda-content-ob
|
||||
import { ExtensionFieldComponent } from './components/extension-field/extension-field.component';
|
||||
import { AttachmentControlComponent } from './components/attachment-control/attachment-control.component';
|
||||
import { RoundedInputComponent } from './components/rounded-input/rounded-input.component';
|
||||
import { ProgressSnackBarComponent } from './components/progress-snack-bar/progress-snack-bar.component';
|
||||
|
||||
/**
|
||||
* Share Module for all "dumb" components and pipes.
|
||||
@ -274,7 +275,8 @@ import { RoundedInputComponent } from './components/rounded-input/rounded-input.
|
||||
AgendaContentObjectFormComponent,
|
||||
ExtensionFieldComponent,
|
||||
AttachmentControlComponent,
|
||||
RoundedInputComponent
|
||||
RoundedInputComponent,
|
||||
ProgressSnackBarComponent
|
||||
],
|
||||
providers: [
|
||||
{ provide: DateAdapter, useClass: OpenSlidesDateAdapter },
|
||||
@ -283,14 +285,16 @@ import { RoundedInputComponent } from './components/rounded-input/rounded-input.
|
||||
SortingTreeComponent,
|
||||
SortFilterBarComponent,
|
||||
SortBottomSheetComponent,
|
||||
DecimalPipe
|
||||
DecimalPipe,
|
||||
ProgressSnackBarComponent
|
||||
],
|
||||
entryComponents: [
|
||||
SortBottomSheetComponent,
|
||||
C4DialogComponent,
|
||||
PromptDialogComponent,
|
||||
ChoiceDialogComponent,
|
||||
ProjectionDialogComponent
|
||||
ProjectionDialogComponent,
|
||||
ProgressSnackBarComponent
|
||||
]
|
||||
})
|
||||
export class SharedModule {}
|
||||
|
@ -12,13 +12,13 @@ import { AgendaFilterListService } from '../../services/agenda-filter-list.servi
|
||||
import { AgendaPdfService } from '../../services/agenda-pdf.service';
|
||||
import { OperatorService } from 'app/core/core-services/operator.service';
|
||||
import { StorageService } from 'app/core/core-services/storage.service';
|
||||
import { PdfDocumentService } from 'app/core/pdf-services/pdf-document.service';
|
||||
import { ItemRepositoryService } from 'app/core/repositories/agenda/item-repository.service';
|
||||
import { ListOfSpeakersRepositoryService } from 'app/core/repositories/agenda/list-of-speakers-repository.service';
|
||||
import { TopicRepositoryService } from 'app/core/repositories/topics/topic-repository.service';
|
||||
import { _ } from 'app/core/translate/translation-marker';
|
||||
import { ConfigService } from 'app/core/ui-services/config.service';
|
||||
import { DurationService } from 'app/core/ui-services/duration.service';
|
||||
import { PdfDocumentService } from 'app/core/ui-services/pdf-document.service';
|
||||
import { PromptService } from 'app/core/ui-services/prompt.service';
|
||||
import { ViewportService } from 'app/core/ui-services/viewport.service';
|
||||
import { ColumnRestriction } from 'app/shared/components/list-view-table/list-view-table.component';
|
||||
|
@ -2,7 +2,7 @@ import { Injectable } from '@angular/core';
|
||||
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
|
||||
import { PdfDocumentService, PdfError } from 'app/core/ui-services/pdf-document.service';
|
||||
import { PdfDocumentService, PdfError } from 'app/core/pdf-services/pdf-document.service';
|
||||
import { AssignmentPdfService } from './assignment-pdf.service';
|
||||
import { ViewAssignment } from '../models/view-assignment';
|
||||
|
||||
|
@ -2,8 +2,7 @@ import { Injectable } from '@angular/core';
|
||||
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
|
||||
import { HtmlToPdfService } from 'app/core/ui-services/html-to-pdf.service';
|
||||
import { PdfDocumentService } from 'app/core/ui-services/pdf-document.service';
|
||||
import { HtmlToPdfService } from 'app/core/pdf-services/html-to-pdf.service';
|
||||
import { PollVoteValue } from 'app/core/ui-services/poll.service';
|
||||
import { AssignmentPollService } from './assignment-poll.service';
|
||||
import { ViewAssignment } from '../models/view-assignment';
|
||||
@ -34,7 +33,6 @@ export class AssignmentPdfService {
|
||||
public constructor(
|
||||
private translate: TranslateService,
|
||||
private pollService: AssignmentPollService,
|
||||
private pdfDocumentService: PdfDocumentService,
|
||||
private htmlToPdfService: HtmlToPdfService
|
||||
) {}
|
||||
|
||||
@ -265,7 +263,7 @@ export class AssignmentPdfService {
|
||||
headerRows: 1,
|
||||
body: pollTableBody
|
||||
},
|
||||
layout: this.pdfDocumentService.switchColorTableLayout
|
||||
layout: 'switchColorTableLayout'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -2,11 +2,11 @@ import { Injectable } from '@angular/core';
|
||||
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
|
||||
import { AbstractPollData, BallotCountChoices, PollPdfService } from 'app/core/pdf-services/base-poll-pdf-service';
|
||||
import { PdfDocumentService } from 'app/core/pdf-services/pdf-document.service';
|
||||
import { AssignmentRepositoryService } from 'app/core/repositories/assignments/assignment-repository.service';
|
||||
import { UserRepositoryService } from 'app/core/repositories/users/user-repository.service';
|
||||
import { ConfigService } from 'app/core/ui-services/config.service';
|
||||
import { PdfDocumentService } from 'app/core/ui-services/pdf-document.service';
|
||||
import { AbstractPollData, BallotCountChoices, PollPdfService } from 'app/core/ui-services/poll-pdf-service';
|
||||
import { AssignmentPollMethod } from './assignment-poll.service';
|
||||
import { ViewAssignmentPoll } from '../models/view-assignment-poll';
|
||||
|
||||
|
@ -8,13 +8,13 @@ import { TranslateService } from '@ngx-translate/core';
|
||||
import { PblColumnDefinition } from '@pebula/ngrid';
|
||||
|
||||
import { StorageService } from 'app/core/core-services/storage.service';
|
||||
import { PdfError } from 'app/core/pdf-services/pdf-document.service';
|
||||
import { CategoryRepositoryService } from 'app/core/repositories/motions/category-repository.service';
|
||||
import { MotionBlockRepositoryService } from 'app/core/repositories/motions/motion-block-repository.service';
|
||||
import { MotionRepositoryService } from 'app/core/repositories/motions/motion-repository.service';
|
||||
import { WorkflowRepositoryService } from 'app/core/repositories/motions/workflow-repository.service';
|
||||
import { TagRepositoryService } from 'app/core/repositories/tags/tag-repository.service';
|
||||
import { ConfigService } from 'app/core/ui-services/config.service';
|
||||
import { PdfError } from 'app/core/ui-services/pdf-document.service';
|
||||
import { ColumnRestriction } from 'app/shared/components/list-view-table/list-view-table.component';
|
||||
import { infoDialogSettings, largeDialogSettings } from 'app/shared/utils/dialog-settings';
|
||||
import { BaseListViewComponent } from 'app/site/base/base-list-view';
|
||||
|
@ -3,10 +3,10 @@ import { Injectable } from '@angular/core';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
|
||||
import { BorderType, PdfDocumentService, PdfError, StyleType } from 'app/core/pdf-services/pdf-document.service';
|
||||
import { CategoryRepositoryService } from 'app/core/repositories/motions/category-repository.service';
|
||||
import { MotionRepositoryService } from 'app/core/repositories/motions/motion-repository.service';
|
||||
import { ConfigService } from 'app/core/ui-services/config.service';
|
||||
import { BorderType, PdfDocumentService, PdfError, StyleType } from 'app/core/ui-services/pdf-document.service';
|
||||
import { ExportFormData } from '../modules/motion-list/components/motion-export-dialog/motion-export-dialog.component';
|
||||
import { MotionPdfService } from './motion-pdf.service';
|
||||
import { ViewCategory } from '../models/view-category';
|
||||
|
@ -2,8 +2,8 @@ import { Injectable } from '@angular/core';
|
||||
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
|
||||
import { PdfDocumentService } from 'app/core/pdf-services/pdf-document.service';
|
||||
import { ConfigService } from 'app/core/ui-services/config.service';
|
||||
import { PdfDocumentService } from 'app/core/ui-services/pdf-document.service';
|
||||
import { PersonalNoteContent } from 'app/shared/models/users/personal-note';
|
||||
import { ExportFormData } from '../modules/motion-list/components/motion-export-dialog/motion-export-dialog.component';
|
||||
import { MotionPdfCatalogService } from './motion-pdf-catalog.service';
|
||||
|
@ -2,14 +2,14 @@ import { Injectable } from '@angular/core';
|
||||
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
|
||||
import { HtmlToPdfService } from 'app/core/pdf-services/html-to-pdf.service';
|
||||
import { PdfDocumentService } from 'app/core/pdf-services/pdf-document.service';
|
||||
import { ChangeRecommendationRepositoryService } from 'app/core/repositories/motions/change-recommendation-repository.service';
|
||||
import { MotionCommentSectionRepositoryService } from 'app/core/repositories/motions/motion-comment-section-repository.service';
|
||||
import { MotionRepositoryService } from 'app/core/repositories/motions/motion-repository.service';
|
||||
import { StatuteParagraphRepositoryService } from 'app/core/repositories/motions/statute-paragraph-repository.service';
|
||||
import { ConfigService } from 'app/core/ui-services/config.service';
|
||||
import { HtmlToPdfService } from 'app/core/ui-services/html-to-pdf.service';
|
||||
import { LinenumberingService } from 'app/core/ui-services/linenumbering.service';
|
||||
import { PdfDocumentService } from 'app/core/ui-services/pdf-document.service';
|
||||
import { CalculablePollKey } from 'app/core/ui-services/poll.service';
|
||||
import { ViewUnifiedChange, ViewUnifiedChangeType } from 'app/shared/models/motions/view-unified-change';
|
||||
import { getRecommendationTypeName } from 'app/shared/utils/recommendation-type-names';
|
||||
@ -547,21 +547,7 @@ export class MotionPdfService {
|
||||
body: metaTableBody
|
||||
},
|
||||
margin: [0, 0, 0, 20],
|
||||
// That did not work too well in the past. Perhaps substitution by a pdfWorker the worker will be necessary
|
||||
layout: {
|
||||
fillColor: () => {
|
||||
return '#dddddd';
|
||||
},
|
||||
hLineWidth: (i, node) => {
|
||||
return i === 0 || i === node.table.body.length ? 0 : 0.5;
|
||||
},
|
||||
vLineWidth: () => {
|
||||
return 0;
|
||||
},
|
||||
hLineColor: () => {
|
||||
return 'white';
|
||||
}
|
||||
}
|
||||
layout: 'metaboxLayout'
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -776,7 +762,7 @@ export class MotionPdfService {
|
||||
dontBreakRows: true,
|
||||
body: callListTableBody.concat(callListRows)
|
||||
},
|
||||
layout: this.pdfDocumentService.switchColorTableLayout
|
||||
layout: 'switchColorTableLayout'
|
||||
};
|
||||
return [title, table];
|
||||
}
|
||||
|
@ -2,11 +2,11 @@ import { Injectable } from '@angular/core';
|
||||
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
|
||||
import { AbstractPollData, PollPdfService } from 'app/core/pdf-services/base-poll-pdf-service';
|
||||
import { PdfDocumentService } from 'app/core/pdf-services/pdf-document.service';
|
||||
import { MotionRepositoryService } from 'app/core/repositories/motions/motion-repository.service';
|
||||
import { UserRepositoryService } from 'app/core/repositories/users/user-repository.service';
|
||||
import { ConfigService } from 'app/core/ui-services/config.service';
|
||||
import { PdfDocumentService } from 'app/core/ui-services/pdf-document.service';
|
||||
import { AbstractPollData, PollPdfService } from 'app/core/ui-services/poll-pdf-service';
|
||||
import { MotionPoll } from 'app/shared/models/motions/motion-poll';
|
||||
|
||||
type BallotCountChoices = 'NUMBER_OF_DELEGATES' | 'NUMBER_OF_ALL_PARTICIPANTS' | 'CUSTOM_NUMBER';
|
||||
|
@ -2,7 +2,7 @@ import { Injectable } from '@angular/core';
|
||||
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
|
||||
import { PdfDocumentService } from 'app/core/ui-services/pdf-document.service';
|
||||
import { PdfDocumentService } from 'app/core/pdf-services/pdf-document.service';
|
||||
import { UserPdfService } from './user-pdf.service';
|
||||
import { ViewUser } from '../models/view-user';
|
||||
|
||||
|
@ -3,7 +3,6 @@ import { Injectable } from '@angular/core';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
|
||||
import { ConfigService } from 'app/core/ui-services/config.service';
|
||||
import { PdfDocumentService } from 'app/core/ui-services/pdf-document.service';
|
||||
import { ViewUser } from '../models/view-user';
|
||||
|
||||
/**
|
||||
@ -25,13 +24,8 @@ export class UserPdfService {
|
||||
*
|
||||
* @param translate handle translations
|
||||
* @param configService Read config variables
|
||||
* @param pdfDocumentService Global PDF Functions
|
||||
*/
|
||||
public constructor(
|
||||
private translate: TranslateService,
|
||||
private configService: ConfigService,
|
||||
private pdfDocumentService: PdfDocumentService
|
||||
) {}
|
||||
public constructor(private translate: TranslateService, private configService: ConfigService) {}
|
||||
|
||||
/**
|
||||
* Converts a user to PdfMake doc definition, containing access information
|
||||
@ -254,7 +248,7 @@ export class UserPdfService {
|
||||
headerRows: 1,
|
||||
body: userTableBody.concat(this.getListUsers(users))
|
||||
},
|
||||
layout: this.pdfDocumentService.switchColorTableLayout
|
||||
layout: 'switchColorTableLayout'
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -8,5 +8,5 @@
|
||||
"enableIvy": false
|
||||
},
|
||||
"include": ["src/**/*.ts"],
|
||||
"exclude": ["src/test.ts", "**/*.spec.ts"]
|
||||
"exclude": ["src/test.ts", "**/*.spec.ts", "src/**/*.worker.ts"]
|
||||
}
|
||||
|
14
client/tsconfig.worker.json
Normal file
14
client/tsconfig.worker.json
Normal file
@ -0,0 +1,14 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "./out-tsc/worker",
|
||||
"lib": [
|
||||
"es2018",
|
||||
"webworker"
|
||||
],
|
||||
"types": []
|
||||
},
|
||||
"include": [
|
||||
"src/**/*.worker.ts"
|
||||
]
|
||||
}
|
Loading…
Reference in New Issue
Block a user