1
- import { Injectable } from '@angular/core' ;
1
+ import { Injectable , Injector } from '@angular/core' ;
2
2
import { notNullOrUndefined } from '@vendure/common/lib/shared-utils' ;
3
3
import {
4
4
BehaviorSubject ,
5
5
combineLatest ,
6
6
first ,
7
- interval ,
8
7
isObservable ,
9
8
Observable ,
10
9
of ,
@@ -13,15 +12,109 @@ import {
13
12
} from 'rxjs' ;
14
13
import { filter , map , startWith , take } from 'rxjs/operators' ;
15
14
import { Permission } from '../../common/generated-types' ;
15
+ import { DataService } from '../../data/providers/data.service' ;
16
+ import { ModalService } from '../modal/modal.service' ;
17
+ import { NotificationService } from '../notification/notification.service' ;
16
18
import { PermissionsService } from '../permissions/permissions.service' ;
17
19
20
+ /**
21
+ * @description
22
+ * The context object which is passed to the `check`, `isAlert` and `action` functions of an
23
+ * {@link AlertConfig} object.
24
+ */
25
+ export interface AlertContext {
26
+ /**
27
+ * @description
28
+ * The Angular [Injector](https://angular.dev/api/core/Injector) which can be used to get instances
29
+ * of services and other providers available in the application.
30
+ */
31
+ injector : Injector ;
32
+ /**
33
+ * @description
34
+ * The [DataService](/reference/admin-ui-api/services/data-service), which provides methods for querying the
35
+ * server-side data.
36
+ */
37
+ dataService : DataService ;
38
+ /**
39
+ * @description
40
+ * The [NotificationService](/reference/admin-ui-api/services/notification-service), which provides methods for
41
+ * displaying notifications to the user.
42
+ */
43
+ notificationService : NotificationService ;
44
+ /**
45
+ * @description
46
+ * The [ModalService](/reference/admin-ui-api/services/modal-service), which provides methods for
47
+ * opening modal dialogs.
48
+ */
49
+ modalService : ModalService ;
50
+ }
51
+
52
+ /**
53
+ * @description
54
+ * A configuration object for an Admin UI alert.
55
+ *
56
+ * @since 2.2.0
57
+ * @docsCategory alerts
58
+ */
18
59
export interface AlertConfig < T = any > {
60
+ /**
61
+ * @description
62
+ * A unique identifier for the alert.
63
+ */
19
64
id : string ;
20
- check : ( ) => T | Promise < T > | Observable < T > ;
21
- recheckIntervalMs ?: number ;
22
- isAlert : ( value : T ) => boolean ;
23
- action : ( data : T ) => void ;
24
- label : ( data : T ) => { text : string ; translationVars ?: { [ key : string ] : string | number } } ;
65
+ /**
66
+ * @description
67
+ * A function which is gets the data used to determine whether the alert should be shown.
68
+ * Typically, this function will query the server or some other remote data source.
69
+ *
70
+ * This function will be called once when the Admin UI app bootstraps, and can be also
71
+ * set to run at regular intervals by setting the `recheckIntervalMs` property.
72
+ */
73
+ check : ( context : AlertContext ) => T | Promise < T > | Observable < T > ;
74
+ /**
75
+ * @description
76
+ * A function which returns an Observable which is used to determine when to re-run the `check`
77
+ * function. Whenever the observable emits, the `check` function will be called again.
78
+ *
79
+ * A basic time-interval-based recheck can be achieved by using the `interval` function from RxJS.
80
+ *
81
+ * @example
82
+ * ```ts
83
+ * import { interval } from 'rxjs';
84
+ *
85
+ * // ...
86
+ * recheck: () => interval(60_000)
87
+ * ```
88
+ *
89
+ * If this is not set, the `check` function will only be called once when the Admin UI app bootstraps.
90
+ *
91
+ * @default undefined
92
+ */
93
+ recheck ?: ( context : AlertContext ) => Observable < any > ;
94
+ /**
95
+ * @description
96
+ * A function which determines whether the alert should be shown based on the data returned by the `check`
97
+ * function.
98
+ */
99
+ isAlert : ( data : T , context : AlertContext ) => boolean ;
100
+ /**
101
+ * @description
102
+ * A function which is called when the alert is clicked in the Admin UI.
103
+ */
104
+ action : ( data : T , context : AlertContext ) => void ;
105
+ /**
106
+ * @description
107
+ * A function which returns the text used in the UI to describe the alert.
108
+ */
109
+ label : (
110
+ data : T ,
111
+ context : AlertContext ,
112
+ ) => { text : string ; translationVars ?: { [ key : string ] : string | number } } ;
113
+ /**
114
+ * @description
115
+ * A list of permissions which the current Administrator must have in order. If the current
116
+ * Administrator does not have these permissions, none of the other alert functions will be called.
117
+ */
25
118
requiredPermissions ?: Permission [ ] ;
26
119
}
27
120
@@ -36,29 +129,32 @@ export class Alert<T> {
36
129
activeAlert$ : Observable < ActiveAlert | undefined > ;
37
130
private hasRun$ = new BehaviorSubject ( false ) ;
38
131
private data$ = new BehaviorSubject < T | undefined > ( undefined ) ;
39
- constructor ( private config : AlertConfig < T > ) {
40
- if ( this . config . recheckIntervalMs ) {
41
- interval ( this . config . recheckIntervalMs ) . subscribe ( ( ) => this . runCheck ( ) ) ;
132
+ constructor (
133
+ private config : AlertConfig < T > ,
134
+ private context : AlertContext ,
135
+ ) {
136
+ if ( this . config . recheck ) {
137
+ this . config . recheck ( this . context ) . subscribe ( ( ) => this . runCheck ( ) ) ;
42
138
}
43
139
this . activeAlert$ = combineLatest ( this . data$ , this . hasRun$ ) . pipe (
44
140
map ( ( [ data , hasRun ] ) => {
45
141
if ( ! data ) {
46
142
return ;
47
143
}
48
- const isAlert = this . config . isAlert ( data ) ;
144
+ const isAlert = this . config . isAlert ( data , this . context ) ;
49
145
if ( ! isAlert ) {
50
146
return ;
51
147
}
52
148
return {
53
149
id : this . config . id ,
54
150
runAction : ( ) => {
55
151
if ( ! hasRun ) {
56
- this . config . action ( data ) ;
152
+ this . config . action ( data , this . context ) ;
57
153
this . hasRun$ . next ( true ) ;
58
154
}
59
155
} ,
60
156
hasRun,
61
- label : this . config . label ( data ) ,
157
+ label : this . config . label ( data , this . context ) ,
62
158
} ;
63
159
} ) ,
64
160
) ;
@@ -67,7 +163,7 @@ export class Alert<T> {
67
163
return this . config . id ;
68
164
}
69
165
runCheck ( ) {
70
- const result = this . config . check ( ) ;
166
+ const result = this . config . check ( this . context ) ;
71
167
if ( result instanceof Promise ) {
72
168
result . then ( data => this . data$ . next ( data ) ) ;
73
169
} else if ( isObservable ( result ) ) {
@@ -87,7 +183,13 @@ export class AlertsService {
87
183
private alertsMap = new Map < string , Alert < any > > ( ) ;
88
184
private configUpdated = new Subject < void > ( ) ;
89
185
90
- constructor ( private permissionsService : PermissionsService ) {
186
+ constructor (
187
+ private permissionsService : PermissionsService ,
188
+ private injector : Injector ,
189
+ private dataService : DataService ,
190
+ private notificationService : NotificationService ,
191
+ private modalService : ModalService ,
192
+ ) {
91
193
const alerts$ = this . configUpdated . pipe (
92
194
map ( ( ) => [ ...this . alertsMap . values ( ) ] ) ,
93
195
startWith ( [ ...this . alertsMap . values ( ) ] ) ,
@@ -108,7 +210,7 @@ export class AlertsService {
108
210
. pipe ( first ( ) )
109
211
. subscribe ( hasPermissions => {
110
212
if ( hasPermissions ) {
111
- this . alertsMap . set ( config . id , new Alert ( config ) ) ;
213
+ this . alertsMap . set ( config . id , new Alert ( config , this . createContext ( ) ) ) ;
112
214
this . configUpdated . next ( ) ;
113
215
}
114
216
} ) ;
@@ -131,4 +233,13 @@ export class AlertsService {
131
233
this . alertsMap . forEach ( config => config . runCheck ( ) ) ;
132
234
}
133
235
}
236
+
237
+ protected createContext ( ) : AlertContext {
238
+ return {
239
+ injector : this . injector ,
240
+ dataService : this . dataService ,
241
+ notificationService : this . notificationService ,
242
+ modalService : this . modalService ,
243
+ } ;
244
+ }
134
245
}
0 commit comments