@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.
- package/package.json +26 -25
- package/src/acute-common-type-guards.ts +12 -0
- package/src/build/ngx-acute-common-info.ts +19 -0
- package/src/components/dialogs/alert/alert.component.ts +41 -0
- package/src/components/dynamic-404-helper/dynamic-404-helper.component.html +24 -0
- package/src/components/dynamic-404-helper/dynamic-404-helper.component.ts +47 -0
- package/src/components/log-display/log-display.component.html +27 -0
- package/src/components/log-display/log-display.component.ts +122 -0
- package/src/components/process-monitor-simple-display/process-monitor-simple-display.component.html +19 -0
- package/src/components/process-monitor-simple-display/process-monitor-simple-display.component.ts +41 -0
- package/src/components/process-monitor-simple-modal-display/process-monitor-simple-modal-display.component.html +21 -0
- package/src/components/process-monitor-simple-modal-display/process-monitor-simple-modal-display.component.ts +41 -0
- package/src/constants.ts +3 -0
- package/src/index.ts +41 -0
- package/src/model/google-analytics-config.ts +3 -0
- package/src/pipes/capitalize.pipe.ts +8 -0
- package/src/pipes/dollar-formatted.pipe.ts +10 -0
- package/src/pipes/duration-ms-formatted.pipe.ts +25 -0
- package/src/pipes/map-values.pipe.ts +19 -0
- package/src/pipes/number-with-commas.pipe.ts +8 -0
- package/src/pipes/order-by.pipe.ts +59 -0
- package/src/pipes/percent-formatted.pipe.ts +9 -0
- package/src/pipes/plural.pipe.ts +9 -0
- package/src/pipes/round.pipe.ts +8 -0
- package/src/pipes/time-ago-formatted.pipe.ts +10 -0
- package/src/pipes/timing.pipe.ts +18 -0
- package/src/services/css-theme.service.spec.ts +17 -0
- package/src/services/css-theme.service.ts +84 -0
- package/src/services/google-analytics.service.spec.ts +17 -0
- package/src/services/google-analytics.service.ts +65 -0
- package/src/services/graphql-query-execution-display-style.ts +5 -0
- package/src/services/graphql-query-execution-options.ts +10 -0
- package/src/services/graphql-query.service.spec.ts +17 -0
- package/src/services/graphql-query.service.ts +85 -0
- package/src/services/local-storage.service.spec.ts +17 -0
- package/src/services/local-storage.service.ts +114 -0
- package/src/services/process-monitor/monitored-processes.ts +5 -0
- package/src/services/process-monitor/process-holder.ts +10 -0
- package/src/services/process-monitor/process-monitor-display-mode.ts +6 -0
- package/src/services/process-monitor/process-monitor-service.ts +178 -0
- package/src/services/process-monitor/process-monitor-state.ts +7 -0
- package/src/services/window-ref.service.spec.ts +17 -0
- 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,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,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,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
|
+
}
|