@mintplayer/ng-spark 22.30.0 → 22.31.0

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 (82) hide show
  1. package/README.md +2 -2
  2. package/auth/README.md +216 -0
  3. package/fesm2022/mintplayer-ng-spark-auth-account.mjs +2023 -0
  4. package/fesm2022/mintplayer-ng-spark-auth-account.mjs.map +1 -0
  5. package/fesm2022/mintplayer-ng-spark-auth-auth-bar.mjs +96 -0
  6. package/fesm2022/mintplayer-ng-spark-auth-auth-bar.mjs.map +1 -0
  7. package/fesm2022/mintplayer-ng-spark-auth-confirm-email.mjs +200 -0
  8. package/fesm2022/mintplayer-ng-spark-auth-confirm-email.mjs.map +1 -0
  9. package/fesm2022/mintplayer-ng-spark-auth-core.mjs +452 -0
  10. package/fesm2022/mintplayer-ng-spark-auth-core.mjs.map +1 -0
  11. package/fesm2022/mintplayer-ng-spark-auth-external-login.mjs +201 -0
  12. package/fesm2022/mintplayer-ng-spark-auth-external-login.mjs.map +1 -0
  13. package/fesm2022/mintplayer-ng-spark-auth-forgot-password.mjs +196 -0
  14. package/fesm2022/mintplayer-ng-spark-auth-forgot-password.mjs.map +1 -0
  15. package/fesm2022/mintplayer-ng-spark-auth-guards.mjs +16 -0
  16. package/fesm2022/mintplayer-ng-spark-auth-guards.mjs.map +1 -0
  17. package/fesm2022/mintplayer-ng-spark-auth-interceptors.mjs +18 -0
  18. package/fesm2022/mintplayer-ng-spark-auth-interceptors.mjs.map +1 -0
  19. package/fesm2022/mintplayer-ng-spark-auth-login.mjs +282 -0
  20. package/fesm2022/mintplayer-ng-spark-auth-login.mjs.map +1 -0
  21. package/fesm2022/mintplayer-ng-spark-auth-models.mjs +114 -0
  22. package/fesm2022/mintplayer-ng-spark-auth-models.mjs.map +1 -0
  23. package/fesm2022/mintplayer-ng-spark-auth-pipes.mjs +43 -0
  24. package/fesm2022/mintplayer-ng-spark-auth-pipes.mjs.map +1 -0
  25. package/fesm2022/mintplayer-ng-spark-auth-register.mjs +216 -0
  26. package/fesm2022/mintplayer-ng-spark-auth-register.mjs.map +1 -0
  27. package/fesm2022/mintplayer-ng-spark-auth-reset-password.mjs +223 -0
  28. package/fesm2022/mintplayer-ng-spark-auth-reset-password.mjs.map +1 -0
  29. package/fesm2022/mintplayer-ng-spark-auth-routes.mjs +194 -0
  30. package/fesm2022/mintplayer-ng-spark-auth-routes.mjs.map +1 -0
  31. package/fesm2022/mintplayer-ng-spark-auth-sign-in.mjs +210 -0
  32. package/fesm2022/mintplayer-ng-spark-auth-sign-in.mjs.map +1 -0
  33. package/fesm2022/mintplayer-ng-spark-auth-two-factor.mjs +218 -0
  34. package/fesm2022/mintplayer-ng-spark-auth-two-factor.mjs.map +1 -0
  35. package/fesm2022/mintplayer-ng-spark-auth.mjs +48 -0
  36. package/fesm2022/mintplayer-ng-spark-auth.mjs.map +1 -0
  37. package/fesm2022/mintplayer-ng-spark-client-operations.mjs +294 -2
  38. package/fesm2022/mintplayer-ng-spark-client-operations.mjs.map +1 -1
  39. package/fesm2022/mintplayer-ng-spark-identity-provider-connected-applications.mjs +389 -0
  40. package/fesm2022/mintplayer-ng-spark-identity-provider-connected-applications.mjs.map +1 -0
  41. package/fesm2022/mintplayer-ng-spark-identity-provider-core.mjs +204 -0
  42. package/fesm2022/mintplayer-ng-spark-identity-provider-core.mjs.map +1 -0
  43. package/fesm2022/mintplayer-ng-spark-identity-provider-developers.mjs +486 -0
  44. package/fesm2022/mintplayer-ng-spark-identity-provider-developers.mjs.map +1 -0
  45. package/fesm2022/mintplayer-ng-spark-identity-provider-management.mjs +350 -0
  46. package/fesm2022/mintplayer-ng-spark-identity-provider-management.mjs.map +1 -0
  47. package/fesm2022/mintplayer-ng-spark-identity-provider.mjs +66 -0
  48. package/fesm2022/mintplayer-ng-spark-identity-provider.mjs.map +1 -0
  49. package/fesm2022/mintplayer-ng-spark-pwa.mjs +162 -0
  50. package/fesm2022/mintplayer-ng-spark-pwa.mjs.map +1 -0
  51. package/fesm2022/mintplayer-ng-spark-services.mjs +11 -1
  52. package/fesm2022/mintplayer-ng-spark-services.mjs.map +1 -1
  53. package/fesm2022/mintplayer-ng-spark-shell.mjs.map +1 -1
  54. package/fesm2022/mintplayer-ng-spark.mjs.map +1 -1
  55. package/package.json +99 -1
  56. package/types/mintplayer-ng-spark-auth-account.d.ts +255 -0
  57. package/types/mintplayer-ng-spark-auth-auth-bar.d.ts +22 -0
  58. package/types/mintplayer-ng-spark-auth-confirm-email.d.ts +21 -0
  59. package/types/mintplayer-ng-spark-auth-core.d.ts +197 -0
  60. package/types/mintplayer-ng-spark-auth-external-login.d.ts +132 -0
  61. package/types/mintplayer-ng-spark-auth-forgot-password.d.ts +18 -0
  62. package/types/mintplayer-ng-spark-auth-guards.d.ts +15 -0
  63. package/types/mintplayer-ng-spark-auth-interceptors.d.ts +2 -0
  64. package/types/mintplayer-ng-spark-auth-login.d.ts +46 -0
  65. package/types/mintplayer-ng-spark-auth-models.d.ts +489 -0
  66. package/types/mintplayer-ng-spark-auth-pipes.d.ts +8 -0
  67. package/types/mintplayer-ng-spark-auth-register.d.ts +21 -0
  68. package/types/mintplayer-ng-spark-auth-reset-password.d.ts +25 -0
  69. package/types/mintplayer-ng-spark-auth-routes.d.ts +154 -0
  70. package/types/mintplayer-ng-spark-auth-sign-in.d.ts +84 -0
  71. package/types/mintplayer-ng-spark-auth-two-factor.d.ts +23 -0
  72. package/types/mintplayer-ng-spark-auth.d.ts +24 -0
  73. package/types/mintplayer-ng-spark-client-operations.d.ts +78 -3
  74. package/types/mintplayer-ng-spark-identity-provider-connected-applications.d.ts +35 -0
  75. package/types/mintplayer-ng-spark-identity-provider-core.d.ts +144 -0
  76. package/types/mintplayer-ng-spark-identity-provider-developers.d.ts +55 -0
  77. package/types/mintplayer-ng-spark-identity-provider-management.d.ts +33 -0
  78. package/types/mintplayer-ng-spark-identity-provider.d.ts +64 -0
  79. package/types/mintplayer-ng-spark-pwa.d.ts +89 -0
  80. package/types/mintplayer-ng-spark-services.d.ts +10 -0
  81. package/types/mintplayer-ng-spark-shell.d.ts +2 -2
  82. package/types/mintplayer-ng-spark.d.ts +4 -4
@@ -0,0 +1,486 @@
1
+ import * as i0 from "@angular/core";
2
+ import { ChangeDetectionStrategy, Component, computed, inject, signal } from "@angular/core";
3
+ import { ActivatedRoute, RouterLink } from "@angular/router";
4
+ import { Color } from "@mintplayer/ng-bootstrap";
5
+ import { BsAlertComponent } from "@mintplayer/ng-bootstrap/alert";
6
+ import { BsCardComponent, BsCardHeaderComponent } from "@mintplayer/ng-bootstrap/card";
7
+ import { BsCheckboxComponent } from "@mintplayer/ng-bootstrap/checkbox";
8
+ import { BsFormComponent } from "@mintplayer/ng-bootstrap/form";
9
+ import { BsSpinnerComponent } from "@mintplayer/ng-bootstrap/spinner";
10
+ import { SparkSecretDialogService } from "@mintplayer/ng-spark/client-operations";
11
+ import { SPARK_IDENTITY_PROVIDER_QUERIES, SparkIdentityProviderDatePipe, SparkIdentityProviderPortalService, SparkIdentityProviderTextPipe, identityProviderQueryLink, injectIdentityProviderText } from "@mintplayer/ng-spark/identity-provider/core";
12
+ var SparkDevelopersComponent = class SparkDevelopersComponent {
13
+ text = injectIdentityProviderText();
14
+ portal = inject(SparkIdentityProviderPortalService);
15
+ secrets = inject(SparkSecretDialogService);
16
+ colors = Color;
17
+ applicationsLink = identityProviderQueryLink(SPARK_IDENTITY_PROVIDER_QUERIES.applications);
18
+ loading = signal(true, ...ngDevMode ? [{ debugName: "loading" }] : /* istanbul ignore next */ []);
19
+ busy = signal(false, ...ngDevMode ? [{ debugName: "busy" }] : /* istanbul ignore next */ []);
20
+ status = signal(null, ...ngDevMode ? [{ debugName: "status" }] : /* istanbul ignore next */ []);
21
+ accepted = signal(false, ...ngDevMode ? [{ debugName: "accepted" }] : /* istanbul ignore next */ []);
22
+ error = signal("", ...ngDevMode ? [{ debugName: "error" }] : /* istanbul ignore next */ []);
23
+ notice = signal("", ...ngDevMode ? [{ debugName: "notice" }] : /* istanbul ignore next */ []);
24
+ statusKey = computed(() => {
25
+ const status = this.status()?.status;
26
+ return status ? `dev.status.${status}` : "dev.status.none";
27
+ }, ...ngDevMode ? [{ debugName: "statusKey" }] : /* istanbul ignore next */ []);
28
+ termsChanged = computed(() => {
29
+ const s = this.status();
30
+ return !!s && (s.status === "Approved" || s.status === "Requested") && s.acceptedTermsVersion !== s.currentTermsVersion;
31
+ }, ...ngDevMode ? [{ debugName: "termsChanged" }] : /* istanbul ignore next */ []);
32
+ canRequest = computed(() => {
33
+ const s = this.status();
34
+ if (!s) return false;
35
+ return !s.status || s.status === "Rejected" || this.termsChanged();
36
+ }, ...ngDevMode ? [{ debugName: "canRequest" }] : /* istanbul ignore next */ []);
37
+ constructor() {
38
+ this.load();
39
+ }
40
+ async load() {
41
+ this.loading.set(true);
42
+ const result = await this.portal.developerStatus();
43
+ if (result.ok) this.status.set(result.value);
44
+ else this.error.set(this.text("loadFailed"));
45
+ this.accepted.set(false);
46
+ this.loading.set(false);
47
+ }
48
+ async request() {
49
+ const status = this.status();
50
+ if (!status || !this.accepted()) return;
51
+ this.busy.set(true);
52
+ this.error.set("");
53
+ this.notice.set("");
54
+ const result = await this.portal.requestDeveloperStatus(status.currentTermsVersion);
55
+ if (result.ok) {
56
+ this.status.set(result.value);
57
+ this.accepted.set(false);
58
+ this.notice.set(this.text(result.value.isActive ? "dev.status.Approved" : "dev.requestSent"));
59
+ } else if (result.status === 409) {
60
+ await this.load();
61
+ this.error.set(this.text("dev.termsChanged"));
62
+ } else if (result.status === 403) this.error.set(this.text("dev.status.Revoked"));
63
+ else this.error.set(this.text("dev.requestFailed"));
64
+ this.busy.set(false);
65
+ }
66
+ async issueToken() {
67
+ this.busy.set(true);
68
+ this.error.set("");
69
+ this.notice.set("");
70
+ const result = await this.portal.issueRegistrationToken();
71
+ if (result.ok) {
72
+ const minutes = Math.max(1, Math.round(result.value.expiresIn / 60));
73
+ this.secrets.show({
74
+ title: this.text("dev.tokenTitle"),
75
+ message: this.text("dev.tokenMessage", minutes),
76
+ value: result.value.initialAccessToken
77
+ });
78
+ } else this.error.set(this.text("dev.tokenFailed"));
79
+ this.busy.set(false);
80
+ }
81
+ static ɵfac = i0.ɵɵngDeclareFactory({
82
+ minVersion: "12.0.0",
83
+ version: "22.2.0",
84
+ ngImport: i0,
85
+ type: SparkDevelopersComponent,
86
+ deps: [],
87
+ target: i0.ɵɵFactoryTarget.Component
88
+ });
89
+ static ɵcmp = i0.ɵɵngDeclareComponent({
90
+ minVersion: "17.0.0",
91
+ version: "22.2.0",
92
+ type: SparkDevelopersComponent,
93
+ isStandalone: true,
94
+ selector: "spark-developers",
95
+ ngImport: i0,
96
+ template: `
97
+ <div class="d-flex justify-content-center">
98
+ <bs-card style="width: 100%; max-width: 760px;">
99
+ <bs-card-header><h3 class="mb-0">{{ 'dev.title' | idp }}</h3></bs-card-header>
100
+ <div class="p-4">
101
+ <p class="text-muted">{{ 'dev.intro' | idp }}</p>
102
+ @if (notice()) {
103
+ <bs-alert [type]="colors.success" class="mb-3 d-block spark-idp-notice">{{ notice() }}</bs-alert>
104
+ }
105
+ @if (error()) {
106
+ <bs-alert [type]="colors.danger" class="mb-3 d-block spark-idp-error">{{ error() }}</bs-alert>
107
+ }
108
+ @if (loading()) {
109
+ <div class="text-center"><bs-spinner /></div>
110
+ } @else if (status(); as s) {
111
+ <p class="fw-semibold spark-developer-status" [attr.data-status]="s.status ?? 'none'">{{ statusKey() | idp }}</p>
112
+ @if (s.requestedAt) { <small class="text-muted d-block">{{ 'dev.requestedAt' | idp: (s.requestedAt | idpDate) }}</small> }
113
+ @if (s.decidedAt) { <small class="text-muted d-block">{{ 'dev.decidedAt' | idp: (s.decidedAt | idpDate) }}</small> }
114
+ @if (s.note) {
115
+ <p class="mt-2 mb-0 spark-developer-note"><span class="text-muted">{{ 'dev.note' | idp }}</span> {{ s.note }}</p>
116
+ }
117
+
118
+ @if (canRequest()) {
119
+ <hr class="my-4" />
120
+ @if (termsChanged()) {
121
+ <p class="spark-developer-terms-changed">{{ 'dev.termsChanged' | idp }}</p>
122
+ }
123
+ @if (s.termsUrl) {
124
+ <p><a [href]="s.termsUrl" target="_blank" rel="noopener noreferrer" class="spark-developer-terms">{{ 'dev.readTerms' | idp }}</a></p>
125
+ }
126
+ <bs-form>
127
+ <div class="mb-3">
128
+ <bs-checkbox [type]="'checkbox'" [name]="'acceptDeveloperTerms'" [isToggled]="accepted()" (isToggledChange)="accepted.set($event === true)">
129
+ {{ 'dev.acceptTerms' | idp: s.currentTermsVersion }}
130
+ </bs-checkbox>
131
+ </div>
132
+ </bs-form>
133
+ <button type="button" class="btn btn-primary spark-developer-request" [disabled]="busy() || !accepted()" (click)="request()">
134
+ @if (busy()) { <bs-spinner class="me-1" /> }
135
+ {{ (termsChanged() ? 'dev.acceptAgain' : 'dev.request') | idp }}
136
+ </button>
137
+ }
138
+
139
+ @if (s.isActive) {
140
+ <hr class="my-4" />
141
+ <h5>{{ 'dev.applicationsTitle' | idp }}</h5>
142
+ <p class="text-muted">{{ 'dev.applicationsHint' | idp }}</p>
143
+ <a class="btn btn-outline-primary spark-developer-applications" [routerLink]="applicationsLink">{{ 'openApplications' | idp }}</a>
144
+
145
+ <h5 class="mt-4">{{ 'dev.registrationTitle' | idp }}</h5>
146
+ <p class="text-muted">{{ 'dev.registrationHint' | idp }}</p>
147
+ <button type="button" class="btn btn-outline-secondary spark-developer-token" [disabled]="busy()" (click)="issueToken()">
148
+ {{ 'dev.issueToken' | idp }}
149
+ </button>
150
+ }
151
+ }
152
+ </div>
153
+ </bs-card>
154
+ </div>
155
+ `,
156
+ isInline: true,
157
+ dependencies: [
158
+ {
159
+ kind: "directive",
160
+ type: RouterLink,
161
+ selector: "[routerLink]",
162
+ inputs: [
163
+ "target",
164
+ "queryParams",
165
+ "fragment",
166
+ "queryParamsHandling",
167
+ "state",
168
+ "info",
169
+ "relativeTo",
170
+ "preserveFragment",
171
+ "skipLocationChange",
172
+ "replaceUrl",
173
+ "browserUrl",
174
+ "routerLink"
175
+ ]
176
+ },
177
+ {
178
+ kind: "component",
179
+ type: BsAlertComponent,
180
+ selector: "bs-alert",
181
+ inputs: [
182
+ "type",
183
+ "announce",
184
+ "isVisible"
185
+ ],
186
+ outputs: ["isVisibleChange", "afterOpenedOrClosed"]
187
+ },
188
+ {
189
+ kind: "component",
190
+ type: BsCardComponent,
191
+ selector: "bs-card",
192
+ inputs: ["color", "outline"]
193
+ },
194
+ {
195
+ kind: "component",
196
+ type: BsCardHeaderComponent,
197
+ selector: "bs-card-header",
198
+ inputs: ["color", "navStyle"]
199
+ },
200
+ {
201
+ kind: "component",
202
+ type: BsCheckboxComponent,
203
+ selector: "bs-checkbox",
204
+ inputs: [
205
+ "type",
206
+ "isToggled",
207
+ "indeterminate",
208
+ "name",
209
+ "value",
210
+ "group"
211
+ ],
212
+ outputs: ["isToggledChange", "indeterminateChange"]
213
+ },
214
+ {
215
+ kind: "component",
216
+ type: BsFormComponent,
217
+ selector: "bs-form",
218
+ inputs: ["action", "method"],
219
+ outputs: ["submitted"]
220
+ },
221
+ {
222
+ kind: "component",
223
+ type: BsSpinnerComponent,
224
+ selector: "bs-spinner",
225
+ inputs: ["type", "color"]
226
+ },
227
+ {
228
+ kind: "pipe",
229
+ type: SparkIdentityProviderTextPipe,
230
+ name: "idp"
231
+ },
232
+ {
233
+ kind: "pipe",
234
+ type: SparkIdentityProviderDatePipe,
235
+ name: "idpDate"
236
+ }
237
+ ],
238
+ changeDetection: i0.ChangeDetectionStrategy.OnPush
239
+ });
240
+ };
241
+ i0.ɵɵngDeclareClassMetadata({
242
+ minVersion: "12.0.0",
243
+ version: "22.2.0",
244
+ ngImport: i0,
245
+ type: SparkDevelopersComponent,
246
+ decorators: [{
247
+ type: Component,
248
+ args: [{
249
+ selector: "spark-developers",
250
+ standalone: true,
251
+ changeDetection: ChangeDetectionStrategy.OnPush,
252
+ imports: [
253
+ RouterLink,
254
+ BsAlertComponent,
255
+ BsCardComponent,
256
+ BsCardHeaderComponent,
257
+ BsCheckboxComponent,
258
+ BsFormComponent,
259
+ BsSpinnerComponent,
260
+ SparkIdentityProviderTextPipe,
261
+ SparkIdentityProviderDatePipe
262
+ ],
263
+ template: `
264
+ <div class="d-flex justify-content-center">
265
+ <bs-card style="width: 100%; max-width: 760px;">
266
+ <bs-card-header><h3 class="mb-0">{{ 'dev.title' | idp }}</h3></bs-card-header>
267
+ <div class="p-4">
268
+ <p class="text-muted">{{ 'dev.intro' | idp }}</p>
269
+ @if (notice()) {
270
+ <bs-alert [type]="colors.success" class="mb-3 d-block spark-idp-notice">{{ notice() }}</bs-alert>
271
+ }
272
+ @if (error()) {
273
+ <bs-alert [type]="colors.danger" class="mb-3 d-block spark-idp-error">{{ error() }}</bs-alert>
274
+ }
275
+ @if (loading()) {
276
+ <div class="text-center"><bs-spinner /></div>
277
+ } @else if (status(); as s) {
278
+ <p class="fw-semibold spark-developer-status" [attr.data-status]="s.status ?? 'none'">{{ statusKey() | idp }}</p>
279
+ @if (s.requestedAt) { <small class="text-muted d-block">{{ 'dev.requestedAt' | idp: (s.requestedAt | idpDate) }}</small> }
280
+ @if (s.decidedAt) { <small class="text-muted d-block">{{ 'dev.decidedAt' | idp: (s.decidedAt | idpDate) }}</small> }
281
+ @if (s.note) {
282
+ <p class="mt-2 mb-0 spark-developer-note"><span class="text-muted">{{ 'dev.note' | idp }}</span> {{ s.note }}</p>
283
+ }
284
+
285
+ @if (canRequest()) {
286
+ <hr class="my-4" />
287
+ @if (termsChanged()) {
288
+ <p class="spark-developer-terms-changed">{{ 'dev.termsChanged' | idp }}</p>
289
+ }
290
+ @if (s.termsUrl) {
291
+ <p><a [href]="s.termsUrl" target="_blank" rel="noopener noreferrer" class="spark-developer-terms">{{ 'dev.readTerms' | idp }}</a></p>
292
+ }
293
+ <bs-form>
294
+ <div class="mb-3">
295
+ <bs-checkbox [type]="'checkbox'" [name]="'acceptDeveloperTerms'" [isToggled]="accepted()" (isToggledChange)="accepted.set($event === true)">
296
+ {{ 'dev.acceptTerms' | idp: s.currentTermsVersion }}
297
+ </bs-checkbox>
298
+ </div>
299
+ </bs-form>
300
+ <button type="button" class="btn btn-primary spark-developer-request" [disabled]="busy() || !accepted()" (click)="request()">
301
+ @if (busy()) { <bs-spinner class="me-1" /> }
302
+ {{ (termsChanged() ? 'dev.acceptAgain' : 'dev.request') | idp }}
303
+ </button>
304
+ }
305
+
306
+ @if (s.isActive) {
307
+ <hr class="my-4" />
308
+ <h5>{{ 'dev.applicationsTitle' | idp }}</h5>
309
+ <p class="text-muted">{{ 'dev.applicationsHint' | idp }}</p>
310
+ <a class="btn btn-outline-primary spark-developer-applications" [routerLink]="applicationsLink">{{ 'openApplications' | idp }}</a>
311
+
312
+ <h5 class="mt-4">{{ 'dev.registrationTitle' | idp }}</h5>
313
+ <p class="text-muted">{{ 'dev.registrationHint' | idp }}</p>
314
+ <button type="button" class="btn btn-outline-secondary spark-developer-token" [disabled]="busy()" (click)="issueToken()">
315
+ {{ 'dev.issueToken' | idp }}
316
+ </button>
317
+ }
318
+ }
319
+ </div>
320
+ </bs-card>
321
+ </div>
322
+ `
323
+ }]
324
+ }],
325
+ ctorParameters: () => []
326
+ });
327
+ var SparkAcceptInvitationComponent = class SparkAcceptInvitationComponent {
328
+ portal = inject(SparkIdentityProviderPortalService);
329
+ route = inject(ActivatedRoute);
330
+ colors = Color;
331
+ applicationsLink = identityProviderQueryLink(SPARK_IDENTITY_PROVIDER_QUERIES.applications);
332
+ token = this.route.snapshot.paramMap.get("token") ?? "";
333
+ applicationId = this.route.snapshot.queryParamMap.get("app") ?? "";
334
+ valid = !!this.token && !!this.applicationId;
335
+ busy = signal(false, ...ngDevMode ? [{ debugName: "busy" }] : /* istanbul ignore next */ []);
336
+ failed = signal(false, ...ngDevMode ? [{ debugName: "failed" }] : /* istanbul ignore next */ []);
337
+ joined = signal(null, ...ngDevMode ? [{ debugName: "joined" }] : /* istanbul ignore next */ []);
338
+ async accept() {
339
+ if (!this.valid) return;
340
+ this.busy.set(true);
341
+ const result = await this.portal.acceptInvitation(this.applicationId, this.token);
342
+ if (result.ok && result.value.accepted) this.joined.set(result.value.application || this.applicationId);
343
+ else this.failed.set(true);
344
+ this.busy.set(false);
345
+ }
346
+ static ɵfac = i0.ɵɵngDeclareFactory({
347
+ minVersion: "12.0.0",
348
+ version: "22.2.0",
349
+ ngImport: i0,
350
+ type: SparkAcceptInvitationComponent,
351
+ deps: [],
352
+ target: i0.ɵɵFactoryTarget.Component
353
+ });
354
+ static ɵcmp = i0.ɵɵngDeclareComponent({
355
+ minVersion: "17.0.0",
356
+ version: "22.2.0",
357
+ type: SparkAcceptInvitationComponent,
358
+ isStandalone: true,
359
+ selector: "spark-accept-invitation",
360
+ ngImport: i0,
361
+ template: `
362
+ <div class="d-flex justify-content-center">
363
+ <bs-card style="width: 100%; max-width: 640px;">
364
+ <bs-card-header><h3 class="mb-0">{{ 'invite.title' | idp }}</h3></bs-card-header>
365
+ <div class="p-4">
366
+ @if (joined(); as application) {
367
+ <bs-alert [type]="colors.success" class="mb-3 d-block spark-invitation-accepted">{{ 'invite.accepted' | idp: application }}</bs-alert>
368
+ <a class="btn btn-outline-primary spark-invitation-applications" [routerLink]="applicationsLink">{{ 'openApplications' | idp }}</a>
369
+ } @else if (!valid || failed()) {
370
+ <bs-alert [type]="colors.danger" class="mb-3 d-block spark-invitation-invalid">{{ 'invite.invalid' | idp }}</bs-alert>
371
+ } @else {
372
+ <p>{{ 'invite.intro' | idp }}</p>
373
+ <button type="button" class="btn btn-primary spark-invitation-accept" [disabled]="busy()" (click)="accept()">
374
+ @if (busy()) { <bs-spinner class="me-1" /> }
375
+ {{ 'invite.accept' | idp }}
376
+ </button>
377
+ }
378
+ </div>
379
+ </bs-card>
380
+ </div>
381
+ `,
382
+ isInline: true,
383
+ dependencies: [
384
+ {
385
+ kind: "directive",
386
+ type: RouterLink,
387
+ selector: "[routerLink]",
388
+ inputs: [
389
+ "target",
390
+ "queryParams",
391
+ "fragment",
392
+ "queryParamsHandling",
393
+ "state",
394
+ "info",
395
+ "relativeTo",
396
+ "preserveFragment",
397
+ "skipLocationChange",
398
+ "replaceUrl",
399
+ "browserUrl",
400
+ "routerLink"
401
+ ]
402
+ },
403
+ {
404
+ kind: "component",
405
+ type: BsAlertComponent,
406
+ selector: "bs-alert",
407
+ inputs: [
408
+ "type",
409
+ "announce",
410
+ "isVisible"
411
+ ],
412
+ outputs: ["isVisibleChange", "afterOpenedOrClosed"]
413
+ },
414
+ {
415
+ kind: "component",
416
+ type: BsCardComponent,
417
+ selector: "bs-card",
418
+ inputs: ["color", "outline"]
419
+ },
420
+ {
421
+ kind: "component",
422
+ type: BsCardHeaderComponent,
423
+ selector: "bs-card-header",
424
+ inputs: ["color", "navStyle"]
425
+ },
426
+ {
427
+ kind: "component",
428
+ type: BsSpinnerComponent,
429
+ selector: "bs-spinner",
430
+ inputs: ["type", "color"]
431
+ },
432
+ {
433
+ kind: "pipe",
434
+ type: SparkIdentityProviderTextPipe,
435
+ name: "idp"
436
+ }
437
+ ],
438
+ changeDetection: i0.ChangeDetectionStrategy.OnPush
439
+ });
440
+ };
441
+ i0.ɵɵngDeclareClassMetadata({
442
+ minVersion: "12.0.0",
443
+ version: "22.2.0",
444
+ ngImport: i0,
445
+ type: SparkAcceptInvitationComponent,
446
+ decorators: [{
447
+ type: Component,
448
+ args: [{
449
+ selector: "spark-accept-invitation",
450
+ standalone: true,
451
+ changeDetection: ChangeDetectionStrategy.OnPush,
452
+ imports: [
453
+ RouterLink,
454
+ BsAlertComponent,
455
+ BsCardComponent,
456
+ BsCardHeaderComponent,
457
+ BsSpinnerComponent,
458
+ SparkIdentityProviderTextPipe
459
+ ],
460
+ template: `
461
+ <div class="d-flex justify-content-center">
462
+ <bs-card style="width: 100%; max-width: 640px;">
463
+ <bs-card-header><h3 class="mb-0">{{ 'invite.title' | idp }}</h3></bs-card-header>
464
+ <div class="p-4">
465
+ @if (joined(); as application) {
466
+ <bs-alert [type]="colors.success" class="mb-3 d-block spark-invitation-accepted">{{ 'invite.accepted' | idp: application }}</bs-alert>
467
+ <a class="btn btn-outline-primary spark-invitation-applications" [routerLink]="applicationsLink">{{ 'openApplications' | idp }}</a>
468
+ } @else if (!valid || failed()) {
469
+ <bs-alert [type]="colors.danger" class="mb-3 d-block spark-invitation-invalid">{{ 'invite.invalid' | idp }}</bs-alert>
470
+ } @else {
471
+ <p>{{ 'invite.intro' | idp }}</p>
472
+ <button type="button" class="btn btn-primary spark-invitation-accept" [disabled]="busy()" (click)="accept()">
473
+ @if (busy()) { <bs-spinner class="me-1" /> }
474
+ {{ 'invite.accept' | idp }}
475
+ </button>
476
+ }
477
+ </div>
478
+ </bs-card>
479
+ </div>
480
+ `
481
+ }]
482
+ }]
483
+ });
484
+ export { SparkAcceptInvitationComponent, SparkDevelopersComponent };
485
+
486
+ //# sourceMappingURL=mintplayer-ng-spark-identity-provider-developers.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mintplayer-ng-spark-identity-provider-developers.mjs","names":[],"sources":["../../identity-provider/developers/src/spark-developers.component.ts","../../identity-provider/developers/src/spark-accept-invitation.component.ts"],"sourcesContent":["import { ChangeDetectionStrategy, Component, computed, inject, signal } from '@angular/core';\nimport { RouterLink } from '@angular/router';\nimport { Color } from '@mintplayer/ng-bootstrap';\nimport { BsAlertComponent } from '@mintplayer/ng-bootstrap/alert';\nimport { BsCardComponent, BsCardHeaderComponent } from '@mintplayer/ng-bootstrap/card';\nimport { BsCheckboxComponent } from '@mintplayer/ng-bootstrap/checkbox';\nimport { BsFormComponent } from '@mintplayer/ng-bootstrap/form';\nimport { BsSpinnerComponent } from '@mintplayer/ng-bootstrap/spinner';\nimport { SparkSecretDialogService } from '@mintplayer/ng-spark/client-operations';\nimport {\n SPARK_IDENTITY_PROVIDER_QUERIES,\n SparkDeveloperStatus,\n SparkIdentityProviderDatePipe,\n SparkIdentityProviderPortalService,\n SparkIdentityProviderTextPipe,\n identityProviderQueryLink,\n injectIdentityProviderText,\n} from '@mintplayer/ng-spark/identity-provider/core';\n\n/**\n * The developer portal's landing page (`/developers`, PRD D2/D7): the user's developer status, the\n * terms to accept to request it (again, when they were raised), and for an active developer a link to\n * their applications (the generic query page, members-only filtered) and a one-time registration\n * token for dynamic client registration (RFC 7591), shown in the same dialog as `showSecret`.\n */\n@Component({\n selector: 'spark-developers',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [\n RouterLink, BsAlertComponent, BsCardComponent, BsCardHeaderComponent, BsCheckboxComponent, BsFormComponent,\n BsSpinnerComponent, SparkIdentityProviderTextPipe, SparkIdentityProviderDatePipe,\n ],\n template: `\n <div class=\"d-flex justify-content-center\">\n <bs-card style=\"width: 100%; max-width: 760px;\">\n <bs-card-header><h3 class=\"mb-0\">{{ 'dev.title' | idp }}</h3></bs-card-header>\n <div class=\"p-4\">\n <p class=\"text-muted\">{{ 'dev.intro' | idp }}</p>\n @if (notice()) {\n <bs-alert [type]=\"colors.success\" class=\"mb-3 d-block spark-idp-notice\">{{ notice() }}</bs-alert>\n }\n @if (error()) {\n <bs-alert [type]=\"colors.danger\" class=\"mb-3 d-block spark-idp-error\">{{ error() }}</bs-alert>\n }\n @if (loading()) {\n <div class=\"text-center\"><bs-spinner /></div>\n } @else if (status(); as s) {\n <p class=\"fw-semibold spark-developer-status\" [attr.data-status]=\"s.status ?? 'none'\">{{ statusKey() | idp }}</p>\n @if (s.requestedAt) { <small class=\"text-muted d-block\">{{ 'dev.requestedAt' | idp: (s.requestedAt | idpDate) }}</small> }\n @if (s.decidedAt) { <small class=\"text-muted d-block\">{{ 'dev.decidedAt' | idp: (s.decidedAt | idpDate) }}</small> }\n @if (s.note) {\n <p class=\"mt-2 mb-0 spark-developer-note\"><span class=\"text-muted\">{{ 'dev.note' | idp }}</span> {{ s.note }}</p>\n }\n\n @if (canRequest()) {\n <hr class=\"my-4\" />\n @if (termsChanged()) {\n <p class=\"spark-developer-terms-changed\">{{ 'dev.termsChanged' | idp }}</p>\n }\n @if (s.termsUrl) {\n <p><a [href]=\"s.termsUrl\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"spark-developer-terms\">{{ 'dev.readTerms' | idp }}</a></p>\n }\n <bs-form>\n <div class=\"mb-3\">\n <bs-checkbox [type]=\"'checkbox'\" [name]=\"'acceptDeveloperTerms'\" [isToggled]=\"accepted()\" (isToggledChange)=\"accepted.set($event === true)\">\n {{ 'dev.acceptTerms' | idp: s.currentTermsVersion }}\n </bs-checkbox>\n </div>\n </bs-form>\n <button type=\"button\" class=\"btn btn-primary spark-developer-request\" [disabled]=\"busy() || !accepted()\" (click)=\"request()\">\n @if (busy()) { <bs-spinner class=\"me-1\" /> }\n {{ (termsChanged() ? 'dev.acceptAgain' : 'dev.request') | idp }}\n </button>\n }\n\n @if (s.isActive) {\n <hr class=\"my-4\" />\n <h5>{{ 'dev.applicationsTitle' | idp }}</h5>\n <p class=\"text-muted\">{{ 'dev.applicationsHint' | idp }}</p>\n <a class=\"btn btn-outline-primary spark-developer-applications\" [routerLink]=\"applicationsLink\">{{ 'openApplications' | idp }}</a>\n\n <h5 class=\"mt-4\">{{ 'dev.registrationTitle' | idp }}</h5>\n <p class=\"text-muted\">{{ 'dev.registrationHint' | idp }}</p>\n <button type=\"button\" class=\"btn btn-outline-secondary spark-developer-token\" [disabled]=\"busy()\" (click)=\"issueToken()\">\n {{ 'dev.issueToken' | idp }}\n </button>\n }\n }\n </div>\n </bs-card>\n </div>\n `,\n})\nexport class SparkDevelopersComponent {\n private readonly text = injectIdentityProviderText();\n private readonly portal = inject(SparkIdentityProviderPortalService);\n private readonly secrets = inject(SparkSecretDialogService);\n protected readonly colors = Color;\n protected readonly applicationsLink = identityProviderQueryLink(SPARK_IDENTITY_PROVIDER_QUERIES.applications);\n\n readonly loading = signal(true);\n readonly busy = signal(false);\n readonly status = signal<SparkDeveloperStatus | null>(null);\n readonly accepted = signal(false);\n readonly error = signal('');\n readonly notice = signal('');\n\n protected readonly statusKey = computed(() => {\n const status = this.status()?.status;\n return status ? `dev.status.${status}` : 'dev.status.none';\n });\n\n /** A developer (or a pending request) whose accepted terms are older than the current ones. */\n protected readonly termsChanged = computed(() => {\n const s = this.status();\n return !!s && (s.status === 'Approved' || s.status === 'Requested') && s.acceptedTermsVersion !== s.currentTermsVersion;\n });\n\n /** Asking (again) is possible unless an administrator revoked the status; the server refuses that too. */\n protected readonly canRequest = computed(() => {\n const s = this.status();\n if (!s) return false;\n return !s.status || s.status === 'Rejected' || this.termsChanged();\n });\n\n constructor() {\n void this.load();\n }\n\n async load(): Promise<void> {\n this.loading.set(true);\n const result = await this.portal.developerStatus();\n if (result.ok) {\n this.status.set(result.value);\n } else {\n this.error.set(this.text('loadFailed'));\n }\n this.accepted.set(false);\n this.loading.set(false);\n }\n\n async request(): Promise<void> {\n const status = this.status();\n if (!status || !this.accepted()) return;\n this.busy.set(true);\n this.error.set('');\n this.notice.set('');\n // The version the page showed: the server refuses (409) one that was raised in the meantime.\n const result = await this.portal.requestDeveloperStatus(status.currentTermsVersion);\n if (result.ok) {\n this.status.set(result.value);\n this.accepted.set(false);\n this.notice.set(this.text(result.value.isActive ? 'dev.status.Approved' : 'dev.requestSent'));\n } else if (result.status === 409) {\n await this.load();\n this.error.set(this.text('dev.termsChanged'));\n } else if (result.status === 403) {\n this.error.set(this.text('dev.status.Revoked'));\n } else {\n this.error.set(this.text('dev.requestFailed'));\n }\n this.busy.set(false);\n }\n\n async issueToken(): Promise<void> {\n this.busy.set(true);\n this.error.set('');\n this.notice.set('');\n const result = await this.portal.issueRegistrationToken();\n if (result.ok) {\n const minutes = Math.max(1, Math.round(result.value.expiresIn / 60));\n this.secrets.show({\n title: this.text('dev.tokenTitle'),\n message: this.text('dev.tokenMessage', minutes),\n value: result.value.initialAccessToken,\n });\n } else {\n this.error.set(this.text('dev.tokenFailed'));\n }\n this.busy.set(false);\n }\n}\n","import { ChangeDetectionStrategy, Component, inject, signal } from '@angular/core';\nimport { ActivatedRoute, RouterLink } from '@angular/router';\nimport { Color } from '@mintplayer/ng-bootstrap';\nimport { BsAlertComponent } from '@mintplayer/ng-bootstrap/alert';\nimport { BsCardComponent, BsCardHeaderComponent } from '@mintplayer/ng-bootstrap/card';\nimport { BsSpinnerComponent } from '@mintplayer/ng-bootstrap/spinner';\nimport {\n SPARK_IDENTITY_PROVIDER_QUERIES,\n SparkIdentityProviderPortalService,\n SparkIdentityProviderTextPipe,\n identityProviderQueryLink,\n} from '@mintplayer/ng-spark/identity-provider/core';\n\n/**\n * The target of a mailed team invitation (`/developers/invitations/:token?app=<applicationId>`, PRD\n * D3). Accepting is an explicit click, not a side effect of opening the link, and needs the signed-in\n * account the invitation was sent to; every failure reads the same, as the server answers it.\n */\n@Component({\n selector: 'spark-accept-invitation',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [RouterLink, BsAlertComponent, BsCardComponent, BsCardHeaderComponent, BsSpinnerComponent, SparkIdentityProviderTextPipe],\n template: `\n <div class=\"d-flex justify-content-center\">\n <bs-card style=\"width: 100%; max-width: 640px;\">\n <bs-card-header><h3 class=\"mb-0\">{{ 'invite.title' | idp }}</h3></bs-card-header>\n <div class=\"p-4\">\n @if (joined(); as application) {\n <bs-alert [type]=\"colors.success\" class=\"mb-3 d-block spark-invitation-accepted\">{{ 'invite.accepted' | idp: application }}</bs-alert>\n <a class=\"btn btn-outline-primary spark-invitation-applications\" [routerLink]=\"applicationsLink\">{{ 'openApplications' | idp }}</a>\n } @else if (!valid || failed()) {\n <bs-alert [type]=\"colors.danger\" class=\"mb-3 d-block spark-invitation-invalid\">{{ 'invite.invalid' | idp }}</bs-alert>\n } @else {\n <p>{{ 'invite.intro' | idp }}</p>\n <button type=\"button\" class=\"btn btn-primary spark-invitation-accept\" [disabled]=\"busy()\" (click)=\"accept()\">\n @if (busy()) { <bs-spinner class=\"me-1\" /> }\n {{ 'invite.accept' | idp }}\n </button>\n }\n </div>\n </bs-card>\n </div>\n `,\n})\nexport class SparkAcceptInvitationComponent {\n private readonly portal = inject(SparkIdentityProviderPortalService);\n private readonly route = inject(ActivatedRoute);\n protected readonly colors = Color;\n protected readonly applicationsLink = identityProviderQueryLink(SPARK_IDENTITY_PROVIDER_QUERIES.applications);\n\n private readonly token = this.route.snapshot.paramMap.get('token') ?? '';\n private readonly applicationId = this.route.snapshot.queryParamMap.get('app') ?? '';\n /** A link without both values cannot be an invitation. */\n protected readonly valid = !!this.token && !!this.applicationId;\n\n readonly busy = signal(false);\n readonly failed = signal(false);\n /** The application joined, once accepted. */\n readonly joined = signal<string | null>(null);\n\n async accept(): Promise<void> {\n if (!this.valid) return;\n this.busy.set(true);\n const result = await this.portal.acceptInvitation(this.applicationId, this.token);\n if (result.ok && result.value.accepted) {\n this.joined.set(result.value.application || this.applicationId);\n } else {\n this.failed.set(true);\n }\n this.busy.set(false);\n }\n}\n"],"mappings":"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"}