Adds support for Hatch print communication to the Angular(6) app.
Migrates the print preferences settings (eg.print.config.*) from
in-Hatch settings to server-stored workstation settings.
Signed-off-by: Bill Erickson <berickxx@gmail.com>
Signed-off-by: blake <blake@mobiusconsortium.org>
// consider moving these to core...
import {FormatService, FormatValuePipe} from '@eg/core/format.service';
+import {HatchService} from '@eg/share/print/hatch.service';
import {PrintService} from '@eg/share/print/print.service';
// Globally available components
providers: [
DatePipe,
CurrencyPipe,
+ HatchService,
PrintService,
FormatService
]
--- /dev/null
+import {Injectable, EventEmitter} from '@angular/core';
+
+export class HatchMessage {
+ msgid: number;
+ resolver: (HatchMessage) => void; // promise resolver
+ rejector: (HatchMessage) => void; // promise rejector
+ status: number;
+ message: string; // error message
+ from: string;
+ action: string;
+ settings: any;
+ content: string;
+ // Response from Hatch.
+ response: any;
+ contentType: string;
+ showDialog: boolean;
+
+ constructor(hash: any) {
+ if (hash) {
+ Object.keys(hash).forEach(key => this[key] = hash[key]);
+ }
+ }
+}
+
+@Injectable()
+export class HatchService {
+
+ isAvailable: boolean;
+ msgId: number;
+ messages: {[msgid:number]: HatchMessage};
+
+ constructor() {
+ this.isAvailable = null;
+ this.messages = {};
+ this.msgId = 1;
+ }
+
+ connect(): boolean {
+
+ if (this.isAvailable !== null) {
+ return this.isAvailable;
+ }
+
+ // When the Hatch extension loads, it tacks an attribute onto
+ // the top-level documentElement to indicate it's available.
+ if (!window.document.documentElement.getAttribute('hatch-is-open')) {
+ console.warn('Could not connect to Hatch');
+ return this.isAvailable = false;
+ }
+
+ window.addEventListener('message', event => {
+
+ // We only accept messages from our own content script.
+ if (event.source !== window) { return; }
+
+ // We only care about messages from the Hatch extension.
+ if (event.data && event.data.from === 'extension') {
+
+ // Avoid logging full Hatch responses. they can get large.
+ console.debug(
+ `Hatch responded to message ID ${event.data.msgid}`);
+
+ this.handleResponse(event.data);
+ }
+ });
+
+ return this.isAvailable = true;
+ }
+
+ // Send a request from the browser to Hatch.
+ sendRequest(msg: HatchMessage): Promise<HatchMessage> {
+ if (this.isAvailable === false) {
+ return Promise.reject('Hatch is not connected');
+ }
+
+ msg.msgid = this.msgId++;
+ msg.from = 'page';
+ this.messages[msg.msgid] = msg;
+ window.postMessage(msg, window.location.origin);
+
+ return new Promise((resolve, reject) => {
+ msg.resolver = resolve;
+ msg.rejector = reject;
+ });
+ }
+
+ // Handle the data sent back to the browser from Hatch.
+ handleResponse(data: any) {
+
+ const msg = this.messages[data.msgid];
+ if (!msg) {
+ console.warn(`No Hatch request found with ID ${data.msgid}`);
+ return;
+ }
+
+ delete this.messages[data.msgid];
+ msg.response = data.content;
+ msg.message = data.message;
+ msg.status = Number(data.status);
+
+ if (msg.status === 200) {
+ msg.resolver(msg);
+ } else {
+ console.error(`Hatch request returned status ${msg.status}`, msg);
+ msg.rejector(msg);
+ }
+ }
+}
+
import {Component, OnInit, TemplateRef, ElementRef, Renderer2} from '@angular/core';
import {PrintService, PrintRequest} from './print.service';
import {StoreService} from '@eg/core/store.service';
+import {ServerStoreService} from '@eg/core/server-store.service';
+import {HatchService, HatchMessage} from './hatch.service';
@Component({
selector: 'eg-print',
private renderer: Renderer2,
private elm: ElementRef,
private store: StoreService,
+ private serverStore: ServerStoreService,
+ private hatch: HatchService,
private printer: PrintService) {
this.isPrinting = false;
this.printQueue = [];
return;
}
- if (printReq.text && true /* !this.hatch.isActive */) {
+ if (printReq.text && !this.useHatch()) {
// Insert HTML into the browser DOM for in-browser printing only.
if (printReq.contentType === 'text/plain') {
show_dialog: printReq.showDialog
});
- if (0 /* this.hatch.isActive */) {
+ if (this.useHatch()) {
this.printViaHatch(printReq);
} else {
// Here the needed HTML is already in the page.
}
}
+ useHatch(): boolean {
+ return this.store.getLocalItem('eg.hatch.enable.printing')
+ && this.hatch.connect();
+ }
+
printViaHatch(printReq: PrintRequest) {
// Send a full HTML document to Hatch
const html = `<html><body>${printReq.text}</body></html>`;
- /*
- this.hatch.print({
- printContext: printReq.printContext,
- content: html
+ this.serverStore.getItem(`eg.print.config.${printReq.printContext}`)
+ .then(config => {
+
+ const msg = new HatchMessage({
+ action: 'print',
+ content: html,
+ settings: config || {},
+ contentType: 'text/html',
+ showDialog: printReq.showDialog
+ });
+
+ this.hatch.sendRequest(msg).then(
+ ok => console.debug('Print request succeeded'),
+ err => console.warn('Print request failed', err)
+ );
});
- */
}
}
<button class="btn btn-secondary" (click)="doPrint()">Test Print</button>
<ng-template #printTemplate let-context>Hello, {{context.world}}!</ng-template>
+<button class="btn btn-secondary" (click)="printWithDialog()">Print with dialog</button>
+
<br/><br/>
<div class="row">
<div class="col-lg-3">
text: '<b>hello</b>',
printContext: 'default'
});
+ }
+ printWithDialog() {
+ this.printer.print({
+ template: this.printTemplate,
+ contextData: {world : this.world},
+ printContext: 'default',
+ showDialog: true
+ });
}
changeDate(date) {
'bool'
);
+
+INSERT INTO config.workstation_setting_type (name, grp, datatype, label)
+VALUES (
+ 'eg.print.config.default', 'gui', 'object',
+ oils_i18n_gettext (
+ 'eg.print.config.default',
+ 'Print config for default context',
+ 'cwst', 'label'
+ )
+), (
+ 'eg.print.config.receipt', 'gui', 'object',
+ oils_i18n_gettext (
+ 'eg.print.config.receipt',
+ 'Print config for receipt context',
+ 'cwst', 'label'
+ )
+), (
+ 'eg.print.config.label', 'gui', 'object',
+ oils_i18n_gettext (
+ 'eg.print.config.label',
+ 'Print config for label context',
+ 'cwst', 'label'
+ )
+), (
+ 'eg.print.config.mail', 'gui', 'object',
+ oils_i18n_gettext (
+ 'eg.print.config.mail',
+ 'Print config for mail context',
+ 'cwst', 'label'
+ )
+), (
+ 'eg.print.config.offline', 'gui', 'object',
+ oils_i18n_gettext (
+ 'eg.print.config.offline',
+ 'Print config for offline context',
+ 'cwst', 'label'
+ )
+);
+
INSERT INTO config.usr_activity_type
(id, ewhat, ehow, egroup, enabled, transient, label)
VALUES (
--- /dev/null
+BEGIN;
+
+--SELECT evergreen.upgrade_deps_block_check('XXXX', :eg_version);
+
+INSERT INTO config.workstation_setting_type (name, grp, datatype, label)
+VALUES (
+ 'eg.print.config.default', 'gui', 'object',
+ oils_i18n_gettext (
+ 'eg.print.config.default',
+ 'Print config for default context',
+ 'cwst', 'label'
+ )
+), (
+ 'eg.print.config.receipt', 'gui', 'object',
+ oils_i18n_gettext (
+ 'eg.print.config.receipt',
+ 'Print config for receipt context',
+ 'cwst', 'label'
+ )
+), (
+ 'eg.print.config.label', 'gui', 'object',
+ oils_i18n_gettext (
+ 'eg.print.config.label',
+ 'Print config for label context',
+ 'cwst', 'label'
+ )
+), (
+ 'eg.print.config.mail', 'gui', 'object',
+ oils_i18n_gettext (
+ 'eg.print.config.mail',
+ 'Print config for mail context',
+ 'cwst', 'label'
+ )
+), (
+ 'eg.print.config.offline', 'gui', 'object',
+ oils_i18n_gettext (
+ 'eg.print.config.offline',
+ 'Print config for offline context',
+ 'cwst', 'label'
+ )
+);
+
+COMMIT;
}
service.getPrintConfig = function(context) {
- return service.getRemoteItem('eg.print.config.' + context);
+ return service.getItem('eg.print.config.' + context);
}
service.setPrintConfig = function(context, config) {
- return service.setRemoteItem('eg.print.config.' + context, config);
+ return service.setItem('eg.print.config.' + context, config);
}
service.getPrinterOptions = function(name) {