@bitblit/ngx-acute-common 6.0.146-alpha → 6.0.148-alpha

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/package.json +26 -25
  2. package/src/acute-common-type-guards.ts +12 -0
  3. package/src/build/ngx-acute-common-info.ts +19 -0
  4. package/src/components/dialogs/alert/alert.component.ts +41 -0
  5. package/src/components/dynamic-404-helper/dynamic-404-helper.component.html +24 -0
  6. package/src/components/dynamic-404-helper/dynamic-404-helper.component.ts +47 -0
  7. package/src/components/log-display/log-display.component.html +27 -0
  8. package/src/components/log-display/log-display.component.ts +122 -0
  9. package/src/components/process-monitor-simple-display/process-monitor-simple-display.component.html +19 -0
  10. package/src/components/process-monitor-simple-display/process-monitor-simple-display.component.ts +41 -0
  11. package/src/components/process-monitor-simple-modal-display/process-monitor-simple-modal-display.component.html +21 -0
  12. package/src/components/process-monitor-simple-modal-display/process-monitor-simple-modal-display.component.ts +41 -0
  13. package/src/constants.ts +3 -0
  14. package/src/index.ts +41 -0
  15. package/src/model/google-analytics-config.ts +3 -0
  16. package/src/pipes/capitalize.pipe.ts +8 -0
  17. package/src/pipes/dollar-formatted.pipe.ts +10 -0
  18. package/src/pipes/duration-ms-formatted.pipe.ts +25 -0
  19. package/src/pipes/map-values.pipe.ts +19 -0
  20. package/src/pipes/number-with-commas.pipe.ts +8 -0
  21. package/src/pipes/order-by.pipe.ts +59 -0
  22. package/src/pipes/percent-formatted.pipe.ts +9 -0
  23. package/src/pipes/plural.pipe.ts +9 -0
  24. package/src/pipes/round.pipe.ts +8 -0
  25. package/src/pipes/time-ago-formatted.pipe.ts +10 -0
  26. package/src/pipes/timing.pipe.ts +18 -0
  27. package/src/services/css-theme.service.spec.ts +17 -0
  28. package/src/services/css-theme.service.ts +84 -0
  29. package/src/services/google-analytics.service.spec.ts +17 -0
  30. package/src/services/google-analytics.service.ts +65 -0
  31. package/src/services/graphql-query-execution-display-style.ts +5 -0
  32. package/src/services/graphql-query-execution-options.ts +10 -0
  33. package/src/services/graphql-query.service.spec.ts +17 -0
  34. package/src/services/graphql-query.service.ts +85 -0
  35. package/src/services/local-storage.service.spec.ts +17 -0
  36. package/src/services/local-storage.service.ts +114 -0
  37. package/src/services/process-monitor/monitored-processes.ts +5 -0
  38. package/src/services/process-monitor/process-holder.ts +10 -0
  39. package/src/services/process-monitor/process-monitor-display-mode.ts +6 -0
  40. package/src/services/process-monitor/process-monitor-service.ts +178 -0
  41. package/src/services/process-monitor/process-monitor-state.ts +7 -0
  42. package/src/services/window-ref.service.spec.ts +17 -0
  43. package/src/services/window-ref.service.ts +34 -0
@@ -0,0 +1,59 @@
1
+ import { Pipe, PipeTransform } from '@angular/core';
2
+
3
+ @Pipe({ name: 'ngxAcuteOderBy', pure: false, standalone: true })
4
+ export class OrderByPipe implements PipeTransform {
5
+ static _orderByComparator(a: any, b: any): number {
6
+ if (isNaN(parseFloat(a)) || !isFinite(a) || isNaN(parseFloat(b)) || !isFinite(b)) {
7
+ //Isn't a number so lowercase the string to properly compare
8
+ if (String(a).toLowerCase() < String(b).toLowerCase()) return -1;
9
+ if (String(a).toLowerCase() > String(b).toLowerCase()) return 1;
10
+ } else {
11
+ //Parse strings as numbers to compare properly
12
+ if (parseFloat(a) < parseFloat(b)) return -1;
13
+ if (parseFloat(a) > parseFloat(b)) return 1;
14
+ }
15
+
16
+ return 0; //equal each other
17
+ }
18
+
19
+ transform(input: any, [config = '+']): any {
20
+ if (!Array.isArray(input)) return input;
21
+
22
+ if (!Array.isArray(config) || (Array.isArray(config) && config.length == 1)) {
23
+ const propertyToCheck: string = !Array.isArray(config) ? config : config[0];
24
+ const desc = propertyToCheck.substr(0, 1) == '-';
25
+
26
+ //Basic array
27
+ if (!propertyToCheck || propertyToCheck == '-' || propertyToCheck == '+') {
28
+ return !desc ? input.sort() : input.sort().reverse();
29
+ } else {
30
+ const property: string =
31
+ propertyToCheck.substr(0, 1) == '+' || propertyToCheck.substr(0, 1) == '-' ? propertyToCheck.substr(1) : propertyToCheck;
32
+
33
+ return input.sort(function (a: any, b: any) {
34
+ return !desc
35
+ ? OrderByPipe._orderByComparator(a[property], b[property])
36
+ : -OrderByPipe._orderByComparator(a[property], b[property]);
37
+ });
38
+ }
39
+ } else {
40
+ //Loop over property of the array in order and sort
41
+ return input.sort(function (a: any, b: any) {
42
+ for (const cEntry of config) {
43
+ //for (let i: number = 0; i < config.length; i++) {
44
+ const desc = cEntry.substr(0, 1) == '-';
45
+ const property = cEntry.substr(0, 1) == '+' || cEntry.substr(0, 1) == '-' ? cEntry.substr(1) : cEntry;
46
+
47
+ const comparison = !desc
48
+ ? OrderByPipe._orderByComparator(a[property], b[property])
49
+ : -OrderByPipe._orderByComparator(a[property], b[property]);
50
+
51
+ //Don't return 0 yet in case of needing to sort by next property
52
+ if (comparison != 0) return comparison;
53
+ }
54
+
55
+ return 0; //equal each other
56
+ });
57
+ }
58
+ }
59
+ }
@@ -0,0 +1,9 @@
1
+ import { Pipe, PipeTransform } from '@angular/core';
2
+ import { NumberRatchet } from '@bitblit/ratchet-common/lang/number-ratchet';
3
+
4
+ @Pipe({ name: 'ngxAcutePercent', standalone: true })
5
+ export class PercentFormattedPipe implements PipeTransform {
6
+ transform(input: number): string {
7
+ return NumberRatchet.pctFormatted(input);
8
+ }
9
+ }
@@ -0,0 +1,9 @@
1
+ import { Pipe, PipeTransform } from '@angular/core';
2
+
3
+ @Pipe({ name: 'ngxAcutePlural', standalone: true })
4
+ export class PluralPipe implements PipeTransform {
5
+ transform(input: number, label: string, pluralLabel: string = ''): string {
6
+ input = input || 0;
7
+ return input === 1 ? `${input} ${label}` : pluralLabel ? `${input} ${pluralLabel}` : `${input} ${label}s`;
8
+ }
9
+ }
@@ -0,0 +1,8 @@
1
+ import { Pipe, PipeTransform } from '@angular/core';
2
+
3
+ @Pipe({ name: 'ngxAcuteRound', standalone: true })
4
+ export class RoundPipe implements PipeTransform {
5
+ transform(input: number): number {
6
+ return Math.round(input);
7
+ }
8
+ }
@@ -0,0 +1,10 @@
1
+ import { Pipe, PipeTransform } from '@angular/core';
2
+ import { DurationRatchet } from '@bitblit/ratchet-common/lang/duration-ratchet';
3
+ import { NumberRatchet } from '@bitblit/ratchet-common/lang/number-ratchet';
4
+
5
+ @Pipe({ name: 'ngxAcuteTimeAgo', standalone: true })
6
+ export class TimeAgoFormattedPipe implements PipeTransform {
7
+ transform(input: number): string {
8
+ return DurationRatchet.formatMsDuration(new Date().getTime() - NumberRatchet.safeNumber(input));
9
+ }
10
+ }
@@ -0,0 +1,18 @@
1
+ import { Pipe, PipeTransform } from '@angular/core';
2
+
3
+ @Pipe({ name: 'ngxAcuteTiming', standalone: true })
4
+ export class TimingPipe implements PipeTransform {
5
+ transform(time: number): string {
6
+ if (time) {
7
+ const minutes = Math.floor(time / 60);
8
+ const seconds = Math.floor(time % 60);
9
+ return `${this.initZero(minutes)}${minutes}:${this.initZero(seconds)}${seconds}`;
10
+ }
11
+
12
+ return '00:00';
13
+ }
14
+
15
+ private initZero(time: number): string {
16
+ return time < 10 ? '0' : '';
17
+ }
18
+ }
@@ -0,0 +1,17 @@
1
+ import { TestBed } from '@angular/core/testing';
2
+ import { beforeEach, describe, expect, test } from 'vitest';
3
+
4
+ import { CssThemeService } from "./css-theme.service";
5
+
6
+ describe('CssThemeService', () => {
7
+ let service: CssThemeService<any>;
8
+
9
+ beforeEach(() => {
10
+ TestBed.configureTestingModule({});
11
+ service = TestBed.inject(CssThemeService);
12
+ });
13
+
14
+ test.skip('should be created', () => {
15
+ expect(service).toBeTruthy();
16
+ });
17
+ });
@@ -0,0 +1,84 @@
1
+ import { Injectable } from '@angular/core';
2
+ import { Logger } from '@bitblit/ratchet-common/logger/logger';
3
+ import { StringRatchet } from "@bitblit/ratchet-common/lang/string-ratchet";
4
+
5
+ /**
6
+ * For changing themes.
7
+ *
8
+ * Steps:
9
+ *
10
+ * In root css setup all the variables youll use like:
11
+ * // Default variable set
12
+ * :root {
13
+ * --twBackgroundColor: #121212;
14
+ * --twTextColor: #E0E0E0;
15
+ * --twStatusLineBackgroundColor: #1E1E1E;
16
+ * --twOffsetBackgroundColor: #1E1E1E;
17
+ * --twStatusLineTextColor: #E0E0E0;
18
+ * --twInputTextColor: #90caf9;
19
+ * --twMainFont: Georgia, serif;
20
+ *
21
+ * --twPlayingWidth: 80vw;
22
+ * --twMainTextSize: 1em;
23
+ * --twSubTextSize: .8em;
24
+ * --twStatusLineTextSize: 1.2em;
25
+ * --twMainHeaderSize: 4em;
26
+ * --twSubHeaderSize: 2em;
27
+ * }
28
+ *
29
+ * Use the variables in your css
30
+ *
31
+ * Then use this to modify them
32
+ *
33
+ *
34
+ *
35
+ */
36
+
37
+ @Injectable({providedIn: 'root'})
38
+ export class CssThemeService<ThemeObject> {
39
+
40
+ public setCssVariable(name: string, value: string, scope: HTMLElement = document.documentElement, prefix: string='--'): void {
41
+ Logger.debug('Setting %s to %s', prefix+name, value);
42
+ scope.style.setProperty(prefix+name, value);
43
+ }
44
+
45
+ public getCssVariable(name: string, scope: HTMLElement = document.documentElement, prefix: string='--'): string | null {
46
+ return StringRatchet.trimToNull(getComputedStyle(scope).getPropertyValue(prefix+name));
47
+ }
48
+
49
+
50
+ public applyTheme(rec: ThemeObject, scope: HTMLElement = document.documentElement, prefix: string='--'): void {
51
+ Object.keys(rec).forEach(k => {
52
+ this.setCssVariable(k, rec[k],scope, prefix);
53
+ })
54
+ }
55
+
56
+ public setNumericVariable(name: string, value: number, suffix: string, scope: HTMLElement = document.documentElement, prefix: string='--'): void {
57
+ this.setCssVariable(name, value+suffix, scope, prefix);
58
+ }
59
+
60
+ public setNumericVariableBetweenBounds(name: string, value: number, minInclusive: number, maxExclusive: number, suffix: string, scope: HTMLElement = document.documentElement, prefix: string='--'): boolean {
61
+ if (value>=minInclusive && value<maxExclusive) {
62
+ this.setCssVariable(name, value+suffix, scope, prefix);
63
+ return true;
64
+ } else {
65
+ Logger.info('Ignoring out of bounds value %d', value);
66
+ return false;
67
+ }
68
+ }
69
+
70
+ public modifyNumericVariableBetweenBounds(name: string, delta: number, min: number, max: number, suffix: string, scope: HTMLElement = document.documentElement, prefix: string='--'): boolean {
71
+ const curValString: string | null = this.getCssVariable(name, scope, prefix);
72
+ if (curValString) {
73
+ const curVal: number = parseInt(curValString.substring(0, curValString.length-suffix.length));
74
+ const newVal: number = curVal + delta;
75
+ return this.setNumericVariableBetweenBounds(name, newVal, min, max, suffix, scope, prefix);
76
+ } else {
77
+ Logger.info('No current value for %s', name);
78
+ return false;
79
+ }
80
+
81
+ }
82
+
83
+
84
+ }
@@ -0,0 +1,17 @@
1
+ import { TestBed } from '@angular/core/testing';
2
+ import { beforeEach, describe, expect, test } from 'vitest';
3
+
4
+ import { GraphqlQueryService } from './graphql-query.service';
5
+
6
+ describe('GraphqlQueryService', () => {
7
+ let service: GraphqlQueryService;
8
+
9
+ beforeEach(() => {
10
+ TestBed.configureTestingModule({});
11
+ service = TestBed.inject(GraphqlQueryService);
12
+ });
13
+
14
+ test.skip('should be created', () => {
15
+ expect(service).toBeTruthy();
16
+ });
17
+ });
@@ -0,0 +1,65 @@
1
+ import { Injectable } from '@angular/core';
2
+ import { NavigationEnd, Router } from '@angular/router';
3
+ import { Logger } from '@bitblit/ratchet-common/logger/logger';
4
+
5
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
6
+ declare let gtag: Function;
7
+
8
+ @Injectable({
9
+ providedIn: 'root',
10
+ })
11
+ // See : https://lumin8media.com/blog/add-google-analytics-angular
12
+ export class GoogleAnalyticsService {
13
+ private static readonly IS_PROD: boolean = true;
14
+
15
+ constructor(private router: Router) {}
16
+
17
+ public initialize() {
18
+ this.onRouteChange();
19
+
20
+ // dynamically add analytics scripts to document head
21
+ try {
22
+ const url: string = 'https://www.googletagmanager.com/gtag/js?id=';
23
+ const gTagScript = document.createElement('script');
24
+ gTagScript.async = true;
25
+ const tagId: string = GoogleAnalyticsService.IS_PROD ? 'G-7D5BBK4K8X' : null;
26
+ gTagScript.src = `${url}${tagId}`;
27
+ document.head.appendChild(gTagScript);
28
+
29
+ const dataLayerScript = document.createElement('script');
30
+ dataLayerScript.innerHTML = `
31
+ window.dataLayer = window.dataLayer || [];
32
+ function gtag(){dataLayer.push(arguments);}
33
+ gtag('js', new Date());
34
+ gtag('config', '${tagId}', {'send_page_view': false});`;
35
+ document.head.appendChild(dataLayerScript);
36
+ } catch (e) {
37
+ Logger.error('Error adding Google Analytics', e, e);
38
+ }
39
+ }
40
+
41
+ // track visited routes
42
+ private onRouteChange() {
43
+ const tagId: string = GoogleAnalyticsService.IS_PROD ? 'G-7D5BBK4K8X' : null;
44
+
45
+ this.router.events.subscribe((event) => {
46
+ if (event instanceof NavigationEnd) {
47
+ gtag('config', tagId, {
48
+ page_path: event.urlAfterRedirects,
49
+ });
50
+
51
+ Logger.info('Sending Google Analytics tracking for: ', event.urlAfterRedirects);
52
+ Logger.info('Google Analytics property ID: ', tagId);
53
+ }
54
+ });
55
+ }
56
+
57
+ // use gtag.js to send Google Analytics Events
58
+ public event(action: string, eventCategory?: string, eventLabel?: string, value?: string) {
59
+ gtag('event', action, {
60
+ ...(eventCategory && { event_category: eventCategory }),
61
+ ...(eventLabel && { event_label: eventLabel }),
62
+ ...(value && { value: value }),
63
+ });
64
+ }
65
+ }
@@ -0,0 +1,5 @@
1
+ export enum GraphqlQueryExecutionDisplayStyle {
2
+ Modal = 'Modal',
3
+ Monitored = 'Monitored',
4
+ Silent = 'Silent',
5
+ }
@@ -0,0 +1,10 @@
1
+ import { AuthorizationStyle } from "@bitblit/ratchet-graphql/graphql/authorization-style";
2
+ import { ErrorHandlingApproach } from "@bitblit/ratchet-common/lang/error-handling-approach";
3
+ import { GraphqlQueryExecutionDisplayStyle } from "./graphql-query-execution-display-style";
4
+
5
+ export interface GraphqlQueryExecutionOptions {
6
+ blockMessage?: string;
7
+ authStyle: AuthorizationStyle;
8
+ errorHandling: ErrorHandlingApproach;
9
+ displayStyle: GraphqlQueryExecutionDisplayStyle;
10
+ }
@@ -0,0 +1,17 @@
1
+ import { TestBed } from '@angular/core/testing';
2
+ import { beforeEach, describe, expect, test } from 'vitest';
3
+
4
+ import { GraphqlQueryService } from './graphql-query.service';
5
+
6
+ describe('GraphqlQueryService', () => {
7
+ let service: GraphqlQueryService;
8
+
9
+ beforeEach(() => {
10
+ TestBed.configureTestingModule({});
11
+ service = TestBed.inject(GraphqlQueryService);
12
+ });
13
+
14
+ test.skip('should be created', () => {
15
+ expect(service).toBeTruthy();
16
+ });
17
+ });
@@ -0,0 +1,85 @@
1
+ import { Injectable } from "@angular/core";
2
+ import { Logger } from "@bitblit/ratchet-common/logger/logger";
3
+ import { GraphqlRatchet } from "@bitblit/ratchet-graphql/graphql/graphql-ratchet";
4
+ import { AuthorizationStyle } from "@bitblit/ratchet-graphql/graphql/authorization-style";
5
+ import { ErrorHandlingApproach } from "@bitblit/ratchet-common/lang/error-handling-approach";
6
+ import { ErrorRatchet } from "@bitblit/ratchet-common/lang/error-ratchet";
7
+ import { LoggerLevelName } from "@bitblit/ratchet-common/logger/logger-level-name";
8
+ import { ProcessMonitorService } from "./process-monitor/process-monitor-service";
9
+ import { GraphqlQueryExecutionOptions } from "./graphql-query-execution-options";
10
+ import { GraphqlQueryExecutionDisplayStyle } from "./graphql-query-execution-display-style";
11
+ import { StringRatchet } from "@bitblit/ratchet-common/lang/string-ratchet";
12
+
13
+ @Injectable({ providedIn: 'root' })
14
+ export class GraphqlQueryService {
15
+ constructor(
16
+ private graphqlRatchet: GraphqlRatchet,
17
+ private processMonitorService: ProcessMonitorService
18
+ ) {}
19
+
20
+ public fullOptions(input?: Partial<GraphqlQueryExecutionOptions>): GraphqlQueryExecutionOptions {
21
+ const rval: GraphqlQueryExecutionOptions = Object.assign({
22
+ blockMessage: null,
23
+ authStyle: AuthorizationStyle.TokenRequired,
24
+ errorHandling: ErrorHandlingApproach.LogAndPassThru,
25
+ displayStyle: GraphqlQueryExecutionDisplayStyle.Monitored
26
+ },input??{});
27
+
28
+ if (StringRatchet.trimToNull(rval.blockMessage) && rval.displayStyle!==GraphqlQueryExecutionDisplayStyle.Modal) {
29
+ throw ErrorRatchet.fErr('Block message specified but display style is not modal');
30
+ }
31
+ return rval;
32
+ }
33
+
34
+ public async executeQuery<T>(
35
+ queryName: string,
36
+ variables: any,
37
+ inOptions?: Partial<GraphqlQueryExecutionOptions>
38
+ ): Promise<T | null> {
39
+ let rval: T | null = null;
40
+ Logger.debug('eq: %j -: %s --: %s ---: %j', queryName, variables);
41
+ const opts: GraphqlQueryExecutionOptions = this.fullOptions(inOptions);
42
+
43
+ try {
44
+ switch (opts.displayStyle) {
45
+ case GraphqlQueryExecutionDisplayStyle.Silent: rval = await this.graphqlRatchet.executeQuery<T>(queryName, variables, opts.authStyle);break;
46
+ case GraphqlQueryExecutionDisplayStyle.Monitored: rval = await this.processMonitorService.monitorProcessSimple(this.graphqlRatchet.executeQuery<T>(queryName, variables, opts.authStyle), 'Running query', false);break;
47
+ case GraphqlQueryExecutionDisplayStyle.Modal: rval = await this.processMonitorService.monitorProcessSimple(
48
+ this.graphqlRatchet.executeQuery<T>(queryName, variables, opts.authStyle),
49
+ opts.blockMessage ?? 'Running query...',true); break;
50
+ default:
51
+ throw ErrorRatchet.fErr('Cannot happen - no such display style as %s', opts.displayStyle);
52
+ }
53
+ } catch (err) {
54
+ ErrorRatchet.handleErrorByApproach(err, opts.errorHandling, LoggerLevelName.error, 'GraphQL Error : %s');
55
+ }
56
+
57
+ return rval;
58
+ }
59
+
60
+ public async executeMutate<T>(
61
+ queryName: string,
62
+ variables: any,
63
+ inOptions?: Partial<GraphqlQueryExecutionOptions>
64
+ ): Promise<T | null> {
65
+ let rval: T | null = null;
66
+ const opts: GraphqlQueryExecutionOptions = this.fullOptions(inOptions);
67
+ Logger.debug('em: %j -: %s --: %s ---: %j', queryName, variables);
68
+ try {
69
+
70
+ switch (opts.displayStyle) {
71
+ case GraphqlQueryExecutionDisplayStyle.Silent: rval = await this.graphqlRatchet.executeMutate<T>(queryName, variables, opts.authStyle);break;
72
+ case GraphqlQueryExecutionDisplayStyle.Monitored: rval = await this.processMonitorService.monitorProcessSimple(this.graphqlRatchet.executeMutate<T>(queryName, variables, opts.authStyle), 'Running query', false);break;
73
+ case GraphqlQueryExecutionDisplayStyle.Modal: rval = await this.processMonitorService.monitorProcessSimple(
74
+ this.graphqlRatchet.executeMutate<T>(queryName, variables, opts.authStyle),
75
+ opts.blockMessage ?? 'Running change...',true); break;
76
+ default:
77
+ throw ErrorRatchet.fErr('Cannot happen - no such display style as %s', opts.displayStyle);
78
+ }
79
+ } catch (err) {
80
+ ErrorRatchet.handleErrorByApproach(err, opts.errorHandling, LoggerLevelName.error, 'GraphQL Error : %s');
81
+ }
82
+ return rval;
83
+ }
84
+
85
+ }
@@ -0,0 +1,17 @@
1
+ import { TestBed } from '@angular/core/testing';
2
+ import { beforeEach, describe, expect, test } from 'vitest';
3
+
4
+ import { LocalStorageService } from './local-storage.service';
5
+
6
+ describe('LocalStorageService', () => {
7
+ let service: LocalStorageService<any>;
8
+
9
+ beforeEach(() => {
10
+ TestBed.configureTestingModule({});
11
+ service = TestBed.inject(LocalStorageService);
12
+ });
13
+
14
+ test.skip('should be created', () => {
15
+ expect(service).toBeTruthy();
16
+ });
17
+ });
@@ -0,0 +1,114 @@
1
+ import { Inject, Injectable } from "@angular/core";
2
+ import { Logger } from '@bitblit/ratchet-common/logger/logger';
3
+ import { ACUTE_APPLICATION_NAME } from "../constants";
4
+ import { RequireRatchet } from "@bitblit/ratchet-common/lang/require-ratchet";
5
+
6
+ /**
7
+ * To use LocalStorageService, you should define a provider with your application
8
+ * name like so:
9
+ *
10
+ * providers: [
11
+ * { provide: ACUTE_APPLICATION_NAME, useValue: 'yourAppName' }
12
+ * ]
13
+ */
14
+
15
+ export function storageFinder(): Storage | null {
16
+ if (typeof window !== 'undefined') {
17
+ if (typeof window.localStorage !== 'undefined') {
18
+ return window.localStorage;
19
+ }
20
+ }
21
+ return null;
22
+ }
23
+
24
+ export function sessionStorageFinder(): Storage | null {
25
+ if (typeof window !== 'undefined') {
26
+ if (typeof window.sessionStorage !== 'undefined') {
27
+ return window.sessionStorage;
28
+ }
29
+ }
30
+ return null;
31
+ }
32
+
33
+
34
+ @Injectable({ providedIn: 'root' })
35
+ export class LocalStorageService<LocalType, SessionType> {
36
+
37
+ constructor(@Inject(ACUTE_APPLICATION_NAME) private appName: string) {
38
+ RequireRatchet.notNullUndefinedOrOnlyWhitespaceString(appName);
39
+ Logger.debug('Starting local storage service with application name %s', this.appName);
40
+ }
41
+
42
+ public get storageReady(): boolean {
43
+ return !!storageFinder();
44
+ }
45
+
46
+ public get sessionStorageReady(): boolean {
47
+ return !!sessionStorageFinder();
48
+ }
49
+
50
+ private get storageName(): string {
51
+ return this.appName+'AcuteLocalStorage';
52
+ }
53
+
54
+ public clear(): void {
55
+ this.update({} as LocalType);
56
+ }
57
+
58
+ public clearSession(): void {
59
+ this.updateSession({} as SessionType);
60
+ }
61
+
62
+ public update(value: LocalType): LocalType {
63
+ return this.updateInternal(value, 'local', storageFinder(), this.storageReady);
64
+ }
65
+
66
+ public updatePartial(value: Partial<LocalType>): LocalType {
67
+ const oldValue: LocalType = this.fetch();
68
+ const newValue: LocalType = Object.assign({}, oldValue, value);
69
+ return this.update(newValue);
70
+ }
71
+
72
+ public fetch(): LocalType {
73
+ return this.fetchInternal('local', storageFinder(), this.storageReady);
74
+ }
75
+
76
+ public updateSession(value: SessionType): SessionType {
77
+ return this.updateInternal(value, 'session', sessionStorageFinder(), this.sessionStorageReady);
78
+ }
79
+
80
+ public updateSessionPartial(value: Partial<SessionType>): SessionType {
81
+ const oldValue: SessionType = this.fetchSession();
82
+ const newValue: SessionType = Object.assign({}, oldValue, value);
83
+ return this.updateSession(newValue);
84
+ }
85
+
86
+ public fetchSession(): SessionType {
87
+ return this.fetchInternal('session', sessionStorageFinder(), this.sessionStorageReady);
88
+ }
89
+
90
+ private updateInternal<T>(value: T, storageLabel: string, storage: Storage, ready: boolean): T {
91
+ if (ready) {
92
+ const toSave: T = value || ({} as T);
93
+ const saveString: string = JSON.stringify(toSave);
94
+ Logger.info('Updating %s to %s',storageLabel,saveString);
95
+ storage.setItem(this.storageName, saveString);
96
+ return toSave;
97
+ } else {
98
+ Logger.info('Skipping update - %s storage not ready : %j', storageLabel, value);
99
+ return {} as T;
100
+ }
101
+ }
102
+
103
+ public fetchInternal<T>(storageLabel: string, storage: Storage, ready: boolean): T {
104
+ if (ready) {
105
+ const loadString: string = storage.getItem(this.storageName) || '{}';
106
+ const rval: T = JSON.parse(loadString) as T;
107
+ return rval;
108
+ } else {
109
+ Logger.info('Skipping %s fetch - storage not ready', storageLabel);
110
+ return {} as T;
111
+ }
112
+ }
113
+
114
+ }
@@ -0,0 +1,5 @@
1
+ import { ProcessHolder } from "./process-holder";
2
+
3
+ export interface MonitoredProcesses {
4
+ processes: ProcessHolder<any>[];
5
+ }
@@ -0,0 +1,10 @@
1
+ import {ProcessMonitorState} from "./process-monitor-state";
2
+ import { WritableSignal } from "@angular/core";
3
+
4
+ export interface ProcessHolder<T> {
5
+ guid: string;
6
+ proc: Promise<T>;
7
+ input: WritableSignal<ProcessMonitorState>;
8
+ group: string;
9
+ modal: boolean;
10
+ }
@@ -0,0 +1,6 @@
1
+
2
+ /** This is all the stuff that can be updated during a process */
3
+ export enum ProcessMonitorDisplayMode {
4
+ Standard = 'Standard',
5
+ Modal = 'Modal',
6
+ }