Skip to content

Commit

Permalink
feat: forecast viewer service
Browse files Browse the repository at this point in the history
  • Loading branch information
chrismclarke committed Mar 1, 2025
1 parent 3cc499f commit cefe7e2
Show file tree
Hide file tree
Showing 4 changed files with 104 additions and 59 deletions.
72 changes: 38 additions & 34 deletions libs/shared/src/features/pdf-viewer/pdf-viewer.component.html
Original file line number Diff line number Diff line change
@@ -1,38 +1,50 @@
@if(legacyBrowser){
<div class="h-full bg-[white]">
@if(serviceReady()){ @if(isCompatible()){
<!-- PDF Viewer -->
<ngx-extended-pdf-viewer
[src]="src"
[page]="page ? page : 1"
[height]="'100%'"
[activeSidebarView]="2"
[textLayer]="false"
[showHandToolButton]="true"
[(sidebarVisible)]="sidebarOpen"
[customToolbar]="customToolbar"
[showBorders]="false"
[mobileFriendlyZoom]="'1.3'"
[textLayer]="false"
[language]="locale"
>
</ngx-extended-pdf-viewer>
} @else {
<!-- Compatibility Warning -->
<ng-container *ngTemplateOutlet="customToolbar"></ng-container>
<div class="bg-[white] mt-4 p-8 text-center">
<div class="error-message">{{ 'PDF Viewer not supported on this device' | translate }}</div>
<div>{{ 'Please install the latest version of Google Chrome and restart' | translate }}</div>
<a
class="mt-2"
mat-raised-button
target="_blank"
href="https://play.google.com/store/apps/details?id=com.android.chrome"
>
<div class="mb-4">{{ 'Please install the latest version of Google Chrome and restart' | translate }}</div>
<a mat-raised-button target="_blank" href="https://play.google.com/store/apps/details?id=com.android.chrome">
{{ 'Update' | translate }}
</a>
<button mat-raised-button (click)="restartApp()">{{ 'Restart App' | translate }}</button>
<button mat-raised-button class="ml-4" mat-button (click)="restartApp()">
{{ 'Restart App' | translate }}
</button>
</div>

} }
<!-- Loading -->
@else {
<ng-container *ngTemplateOutlet="customToolbar"></ng-container>
<div class="text-center mt-4">{{ 'Loading' | translate }}...</div>
}
</div>

} @else {
<ngx-extended-pdf-viewer
[src]="src"
[page]="page ? page : 1"
[height]="'100%'"
[activeSidebarView]="2"
[textLayer]="false"
[showHandToolButton]="true"
[(sidebarVisible)]="sidebarOpen"
[customToolbar]="customToolbar"
[showBorders]="false"
[mobileFriendlyZoom]="'1.3'"
[textLayer]="false"
[language]="locale"
>
</ngx-extended-pdf-viewer>
}
<!-- Custom Sidebar -->
<!-- <ng-template #customSidebar>
<div id="sidebarContainer" style="top: 8px">
<pdf-sidebar-content></pdf-sidebar-content>
<div id="sidebarResizer" class="hidden"></div>
</div>
</ng-template> -->

<!-- Custom toolbar examples here: https://pdfviewer.net/extended-pdf-viewer/custom-toolbar -->
<ng-template #customToolbar>
Expand All @@ -55,11 +67,3 @@
</div>
</div>
</ng-template>

<!-- Custom Sidebar -->
<!-- <ng-template #customSidebar>
<div id="sidebarContainer" style="top: 8px">
<pdf-sidebar-content></pdf-sidebar-content>
<div id="sidebarResizer" class="hidden"></div>
</div>
</ng-template> -->
42 changes: 18 additions & 24 deletions libs/shared/src/features/pdf-viewer/pdf-viewer.component.ts
Original file line number Diff line number Diff line change
@@ -1,52 +1,46 @@
import { CommonModule } from '@angular/common';
import { Component, Input, ViewEncapsulation } from '@angular/core';
import { AfterViewInit, Component, Input, signal, ViewEncapsulation } from '@angular/core';
import { toSignal } from '@angular/core/rxjs-interop';
import { MatButtonModule } from '@angular/material/button';
import { App } from '@capacitor/app';
import { Capacitor } from '@capacitor/core';
import { pdfDefaultOptions, PDFScriptLoaderService } from 'ngx-extended-pdf-viewer';
import { pdfDefaultOptions } from 'ngx-extended-pdf-viewer';
import { NgxExtendedPdfViewerModule } from 'ngx-extended-pdf-viewer';

import { PicsaTranslateModule } from '../../modules';
import { PicsaPDFViewerService } from './pdf-viewer.service';

@Component({
selector: 'picsa-pdf-viewer',
templateUrl: './pdf-viewer.component.html',
styleUrls: ['./pdf-viewer.component.scss'],
encapsulation: ViewEncapsulation.None,
imports: [NgxExtendedPdfViewerModule, MatButtonModule, CommonModule],
imports: [NgxExtendedPdfViewerModule, MatButtonModule, CommonModule, PicsaTranslateModule],
})
export class PdfViewerComponent {
legacyBrowser = true;
export class PdfViewerComponent implements AfterViewInit {
sidebarOpen = false;
// additional locales are currently excluded from main build
locale = 'en-GB';
public isNative = Capacitor.isNativePlatform();
@Input() page?: number;
@Input() src: string;
constructor() {

public serviceReady = toSignal(this.service.ready$, { initialValue: false });
public isCompatible = signal(false);

constructor(public service: PicsaPDFViewerService) {
// name of folder pdf viewer assets copied to as declared in `angular.json`
pdfDefaultOptions.assetsFolder = 'assets/pdf-viewer';
// force viewer to not use es5 fallback (not included in build)
// use comparable check below to share message if not available for legacy browser
this.runCompatibilityCheck();
}

public restartApp() {
App.exitApp();
async ngAfterViewInit() {
await this.service.ready();
this.isCompatible.set(this.service.isCompatible);
}

private async runCompatibilityCheck() {
// use same check that ngx-extended-pdf calls when checking compatibility locally

// NOTE - as of v19 requires quite modern features available in native browser (pdfJS v4.1+),
// and cannot be polyfilled as required by worker/iframe. E.g. Promise.withResolvers
// https://github.com/stephanrauh/ngx-extended-pdf-viewer/issues/2500
// https://github.com/mozilla/pdf.js/pull/17854
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/withResolvers#browser_compatibility

// This is currently known to be resolved as of chrome 119

// Use inline scripts. Note if Using CSP security should set to false and include
// `op-chaining-support.js` in list of assets copied to run compatibility checks from js file instead of inline
const useInlineScripts = true;
this.legacyBrowser = await new PDFScriptLoaderService(null as any, null as any)['needsES5'](useInlineScripts);
public restartApp() {
App.exitApp();
}
}
45 changes: 45 additions & 0 deletions libs/shared/src/features/pdf-viewer/pdf-viewer.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { Injectable, Injector, runInInjectionContext } from '@angular/core';
import { PDFScriptLoaderService } from 'ngx-extended-pdf-viewer';

import { PicsaAsyncService } from '../../services/asyncService.service';
import { ErrorHandlerService } from '../../services/core/error-handler.service';

@Injectable({ providedIn: 'root' })
export class PicsaPDFViewerService extends PicsaAsyncService {
public isCompatible = false;

constructor(private injector: Injector, private errorService: ErrorHandlerService) {
super();
}

public override async init() {
await this.runCompatibilityCheck();
}

private async runCompatibilityCheck() {
// Use same check that ngx-extended-pdf calls when checking compatibility locally
// As es5 bundles not included will fallback to update prompt in UI if compatibility fails

// NOTE - as of v19 requires quite modern features available in native browser (pdfJS v4.1+),
// and cannot be polyfilled as required by worker/iframe. E.g. Promise.withResolvers
// https://github.com/stephanrauh/ngx-extended-pdf-viewer/issues/2500
// https://github.com/mozilla/pdf.js/pull/17854
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/withResolvers#browser_compatibility

// This is currently known to be resolved as of chrome 119

// Use inline scripts. Note if Using CSP security should set to false and include
// `op-chaining-support.js` in list of assets copied to run compatibility checks from js file instead of inline
const useInlineScripts = true;
try {
// As ngx-extended viewer uses effect signal for it's own service use injection context to ensure availability
runInInjectionContext(this.injector, async () => {
const needsES5 = await new PDFScriptLoaderService(null as any, null as any)['needsES5'](useInlineScripts);
this.isCompatible = !needsES5;
});
} catch (error) {
this.isCompatible = false;
this.errorService.handleError(error as any);
}
}
}
4 changes: 3 additions & 1 deletion libs/shared/src/services/asyncService.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ export class PicsaAsyncService {
this.callInitFunction();
}
}
/** Observable for service ready state */
public ready$ = this.initialised$.pipe(filter((v) => v === true));

private callInitFunction(...args: any) {
this.initCalled = true;
Expand All @@ -45,7 +47,7 @@ export class PicsaAsyncService {
if (!this.initCalled) {
this.callInitFunction(args);
}
return firstValueFrom(this.initialised$.pipe(filter((v) => v === true)));
return firstValueFrom(this.ready$);
};

/** Specify any async initialisation logic in method */
Expand Down

0 comments on commit cefe7e2

Please sign in to comment.