@mintplayer/ng-spark 22.32.0 → 22.33.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 (75) hide show
  1. package/README.md +101 -1
  2. package/fesm2022/mintplayer-ng-spark-attribute-description.mjs +3 -3
  3. package/fesm2022/mintplayer-ng-spark-auth-account.mjs +18 -18
  4. package/fesm2022/mintplayer-ng-spark-auth-auth-bar.mjs +16 -8
  5. package/fesm2022/mintplayer-ng-spark-auth-auth-bar.mjs.map +1 -1
  6. package/fesm2022/mintplayer-ng-spark-auth-confirm-email.mjs +3 -3
  7. package/fesm2022/mintplayer-ng-spark-auth-core.mjs +6 -6
  8. package/fesm2022/mintplayer-ng-spark-auth-external-login.mjs +3 -3
  9. package/fesm2022/mintplayer-ng-spark-auth-forgot-password.mjs +3 -3
  10. package/fesm2022/mintplayer-ng-spark-auth-login.mjs +3 -3
  11. package/fesm2022/mintplayer-ng-spark-auth-pipes.mjs +3 -3
  12. package/fesm2022/mintplayer-ng-spark-auth-register.mjs +3 -3
  13. package/fesm2022/mintplayer-ng-spark-auth-reset-password.mjs +3 -3
  14. package/fesm2022/mintplayer-ng-spark-auth-sign-in.mjs +3 -3
  15. package/fesm2022/mintplayer-ng-spark-auth-two-factor.mjs +3 -3
  16. package/fesm2022/mintplayer-ng-spark-client-operations.mjs +24 -24
  17. package/fesm2022/mintplayer-ng-spark-column-filter.mjs +3 -3
  18. package/fesm2022/mintplayer-ng-spark-contributions.mjs +9 -9
  19. package/fesm2022/mintplayer-ng-spark-grid.mjs +220 -49
  20. package/fesm2022/mintplayer-ng-spark-grid.mjs.map +1 -1
  21. package/fesm2022/mintplayer-ng-spark-history.mjs +9 -9
  22. package/fesm2022/mintplayer-ng-spark-icon.mjs +3 -3
  23. package/fesm2022/mintplayer-ng-spark-identity-provider-connected-applications.mjs +3 -3
  24. package/fesm2022/mintplayer-ng-spark-identity-provider-core.mjs +9 -9
  25. package/fesm2022/mintplayer-ng-spark-identity-provider-developers.mjs +6 -6
  26. package/fesm2022/mintplayer-ng-spark-identity-provider-management.mjs +3 -3
  27. package/fesm2022/mintplayer-ng-spark-models.mjs +44 -1
  28. package/fesm2022/mintplayer-ng-spark-models.mjs.map +1 -1
  29. package/fesm2022/mintplayer-ng-spark-moderation.mjs +21 -21
  30. package/fesm2022/mintplayer-ng-spark-notifications.mjs +687 -0
  31. package/fesm2022/mintplayer-ng-spark-notifications.mjs.map +1 -0
  32. package/fesm2022/mintplayer-ng-spark-organizations-accept.mjs +179 -0
  33. package/fesm2022/mintplayer-ng-spark-organizations-accept.mjs.map +1 -0
  34. package/fesm2022/mintplayer-ng-spark-organizations.mjs +202 -0
  35. package/fesm2022/mintplayer-ng-spark-organizations.mjs.map +1 -0
  36. package/fesm2022/mintplayer-ng-spark-pipes.mjs +125 -85
  37. package/fesm2022/mintplayer-ng-spark-pipes.mjs.map +1 -1
  38. package/fesm2022/mintplayer-ng-spark-po-create.mjs +3 -3
  39. package/fesm2022/mintplayer-ng-spark-po-detail.mjs +87 -28
  40. package/fesm2022/mintplayer-ng-spark-po-detail.mjs.map +1 -1
  41. package/fesm2022/mintplayer-ng-spark-po-edit.mjs +84 -12
  42. package/fesm2022/mintplayer-ng-spark-po-edit.mjs.map +1 -1
  43. package/fesm2022/mintplayer-ng-spark-po-form.mjs +9 -9
  44. package/fesm2022/mintplayer-ng-spark-pwa.mjs +74 -6
  45. package/fesm2022/mintplayer-ng-spark-pwa.mjs.map +1 -1
  46. package/fesm2022/mintplayer-ng-spark-query-list.mjs +10 -109
  47. package/fesm2022/mintplayer-ng-spark-query-list.mjs.map +1 -1
  48. package/fesm2022/mintplayer-ng-spark-renderers.mjs +3 -3
  49. package/fesm2022/mintplayer-ng-spark-retry-action-modal.mjs +3 -3
  50. package/fesm2022/mintplayer-ng-spark-score-renderers.mjs +801 -0
  51. package/fesm2022/mintplayer-ng-spark-score-renderers.mjs.map +1 -0
  52. package/fesm2022/mintplayer-ng-spark-services.mjs +424 -72
  53. package/fesm2022/mintplayer-ng-spark-services.mjs.map +1 -1
  54. package/fesm2022/mintplayer-ng-spark-share-page.mjs +334 -0
  55. package/fesm2022/mintplayer-ng-spark-share-page.mjs.map +1 -0
  56. package/fesm2022/mintplayer-ng-spark-share.mjs +10 -0
  57. package/fesm2022/mintplayer-ng-spark-share.mjs.map +1 -0
  58. package/fesm2022/mintplayer-ng-spark-shell.mjs +38 -36
  59. package/fesm2022/mintplayer-ng-spark-shell.mjs.map +1 -1
  60. package/fesm2022/mintplayer-ng-spark-soft-delete.mjs +9 -9
  61. package/package.json +25 -1
  62. package/types/mintplayer-ng-spark-grid.d.ts +77 -26
  63. package/types/mintplayer-ng-spark-models.d.ts +150 -17
  64. package/types/mintplayer-ng-spark-notifications.d.ts +188 -0
  65. package/types/mintplayer-ng-spark-organizations-accept.d.ts +40 -0
  66. package/types/mintplayer-ng-spark-organizations.d.ts +72 -0
  67. package/types/mintplayer-ng-spark-pipes.d.ts +10 -0
  68. package/types/mintplayer-ng-spark-po-detail.d.ts +26 -0
  69. package/types/mintplayer-ng-spark-po-edit.d.ts +37 -0
  70. package/types/mintplayer-ng-spark-pwa.d.ts +48 -2
  71. package/types/mintplayer-ng-spark-query-list.d.ts +8 -23
  72. package/types/mintplayer-ng-spark-score-renderers.d.ts +340 -0
  73. package/types/mintplayer-ng-spark-services.d.ts +146 -6
  74. package/types/mintplayer-ng-spark-share-page.d.ts +67 -0
  75. package/types/mintplayer-ng-spark-share.d.ts +26 -0
@@ -0,0 +1,687 @@
1
+ import * as i0 from "@angular/core";
2
+ import { ChangeDetectionStrategy, Component, DestroyRef, Injectable, InjectionToken, PLATFORM_ID, computed, effect, inject, input, signal, untracked } from "@angular/core";
3
+ import { DOCUMENT, Location, isPlatformBrowser } from "@angular/common";
4
+ import { valueFor } from "@mintplayer/ng-spark/models";
5
+ import { HttpClient, HttpErrorResponse } from "@angular/common/http";
6
+ import { Router, RouterLink } from "@angular/router";
7
+ import { SwPush } from "@angular/service-worker";
8
+ import { Subscription, firstValueFrom } from "rxjs";
9
+ import { SPARK_AUTH_STATE, SPARK_CONFIG } from "@mintplayer/ng-spark";
10
+ import { SparkLanguageService, SparkLiveService, SparkService } from "@mintplayer/ng-spark/services";
11
+ import { sparkInAppUrl } from "@mintplayer/ng-spark/pwa";
12
+ import { CdkConnectedOverlay, CdkOverlayOrigin } from "@angular/cdk/overlay";
13
+ import { Color } from "@mintplayer/ng-bootstrap";
14
+ import { BsBadgeComponent } from "@mintplayer/ng-bootstrap/badge";
15
+ import { BsDropdownDividerDirective, BsDropdownHeaderDirective, BsDropdownItemDirective, BsDropdownMenuComponent } from "@mintplayer/ng-bootstrap/dropdown-menu";
16
+ import { SparkIconComponent } from "@mintplayer/ng-spark/icon";
17
+ const SPARK_NOTIFICATION_TYPE_ID = "411e3be6-5da0-5d84-bc21-001888fafc6d";
18
+ const SPARK_MY_NOTIFICATIONS_QUERY_ID = "7123c201-9aed-50f9-9d9a-ede2491af7f9";
19
+ const SPARK_UNREAD_NOTIFICATIONS_QUERY_ID = "a205d3e5-ff6e-5260-ac9e-e5c6b16644c9";
20
+ function toSparkNotification(item) {
21
+ const text = (name) => {
22
+ const value = valueFor(item, name)?.value;
23
+ return value == null || value === "" ? null : String(value);
24
+ };
25
+ const created = text("CreatedAtUtc");
26
+ const createdAt = created ? new Date(created) : null;
27
+ return {
28
+ id: item.id,
29
+ title: text("Title") ?? item.breadcrumb ?? "",
30
+ body: text("Body"),
31
+ url: text("Url"),
32
+ isRead: valueFor(item, "IsRead")?.value === true,
33
+ createdAt: createdAt && !Number.isNaN(createdAt.getTime()) ? createdAt : null
34
+ };
35
+ }
36
+ const SPARK_NOTIFICATIONS_OPTIONS = new InjectionToken("SPARK_NOTIFICATIONS_OPTIONS", {
37
+ providedIn: "root",
38
+ factory: () => ({
39
+ recentCount: 8,
40
+ listLink: ["/query", "notifications"]
41
+ })
42
+ });
43
+ function provideSparkNotifications(options = {}) {
44
+ return {
45
+ provide: SPARK_NOTIFICATIONS_OPTIONS,
46
+ useValue: {
47
+ recentCount: 8,
48
+ listLink: ["/query", "notifications"],
49
+ ...options
50
+ }
51
+ };
52
+ }
53
+ const SPARK_PUSH_PERMISSIONS = new InjectionToken("SPARK_PUSH_PERMISSIONS", {
54
+ providedIn: "root",
55
+ factory: () => {
56
+ const window = inject(DOCUMENT).defaultView;
57
+ if (!window || !window.Notification || !window.PushManager || !window.navigator?.serviceWorker) return null;
58
+ return window.Notification;
59
+ }
60
+ });
61
+ var SparkNotificationsService = class SparkNotificationsService {
62
+ http = inject(HttpClient);
63
+ live = inject(SparkLiveService);
64
+ spark = inject(SparkService);
65
+ router = inject(Router, { optional: true });
66
+ location = inject(Location, { optional: true });
67
+ document = inject(DOCUMENT);
68
+ swPush = inject(SwPush, { optional: true });
69
+ permissions = inject(SPARK_PUSH_PERMISSIONS);
70
+ options = inject(SPARK_NOTIFICATIONS_OPTIONS);
71
+ baseUrl = `${inject(SPARK_CONFIG, { optional: true })?.baseUrl ?? "/spark"}/notifications`;
72
+ browser = isPlatformBrowser(inject(PLATFORM_ID));
73
+ unread = signal(null, ...ngDevMode ? [{ debugName: "unread" }] : /* istanbul ignore next */ []);
74
+ latest = signal(null, ...ngDevMode ? [{ debugName: "latest" }] : /* istanbul ignore next */ []);
75
+ unreadCount = computed(() => this.unread()?.totalItems ?? 0, ...ngDevMode ? [{ debugName: "unreadCount" }] : /* istanbul ignore next */ []);
76
+ recent = computed(() => (this.latest()?.items ?? []).map(toSparkNotification), ...ngDevMode ? [{ debugName: "recent" }] : /* istanbul ignore next */ []);
77
+ loaded = computed(() => this.latest() !== null, ...ngDevMode ? [{ debugName: "loaded" }] : /* istanbul ignore next */ []);
78
+ pushSupported = this.browser && !!this.swPush?.isEnabled && this.permissions !== null;
79
+ permission = signal(this.permissions?.permission ?? "default", ...ngDevMode ? [{ debugName: "permission" }] : /* istanbul ignore next */ []);
80
+ subscribed = signal(false, ...ngDevMode ? [{ debugName: "subscribed" }] : /* istanbul ignore next */ []);
81
+ configured = signal(null, ...ngDevMode ? [{ debugName: "configured" }] : /* istanbul ignore next */ []);
82
+ busy = signal(false, ...ngDevMode ? [{ debugName: "busy" }] : /* istanbul ignore next */ []);
83
+ publicKey = null;
84
+ pushState = computed(() => {
85
+ if (!this.pushSupported) return "unsupported";
86
+ if (this.permission() === "denied") return "denied";
87
+ return this.subscribed() && this.permission() === "granted" ? "on" : "off";
88
+ }, ...ngDevMode ? [{ debugName: "pushState" }] : /* istanbul ignore next */ []);
89
+ pushConfigured = this.configured.asReadonly();
90
+ pushBusy = this.busy.asReadonly();
91
+ constructor() {
92
+ if (!this.browser) return;
93
+ const authState = inject(SPARK_AUTH_STATE, { optional: true });
94
+ effect((onCleanup) => {
95
+ authState?.();
96
+ untracked(() => {
97
+ const subscriptions = new Subscription();
98
+ subscriptions.add(this.follow({
99
+ queryId: SPARK_UNREAD_NOTIFICATIONS_QUERY_ID,
100
+ skip: 0,
101
+ take: 1
102
+ }, this.unread));
103
+ subscriptions.add(this.follow({
104
+ queryId: SPARK_MY_NOTIFICATIONS_QUERY_ID,
105
+ skip: 0,
106
+ take: this.options.recentCount
107
+ }, this.latest));
108
+ this.publicKey = null;
109
+ this.loadPublicKey();
110
+ onCleanup(() => {
111
+ subscriptions.unsubscribe();
112
+ this.unread.set(null);
113
+ this.latest.set(null);
114
+ });
115
+ });
116
+ });
117
+ if (this.swPush?.isEnabled) {
118
+ const subscription = this.swPush.subscription.subscribe((current) => this.subscribed.set(!!current));
119
+ subscription.add(this.swPush.notificationClicks.subscribe(({ notification }) => {
120
+ const id = notification?.data?.notificationId;
121
+ if (typeof id === "string" && id) this.markRead([id]).catch(() => void 0);
122
+ }));
123
+ inject(DestroyRef).onDestroy(() => subscription.unsubscribe());
124
+ }
125
+ }
126
+ async markRead(ids) {
127
+ const body = ids ? { ids: [...ids] } : {};
128
+ return (await firstValueFrom(this.http.post(`${this.baseUrl}/read`, body)))?.marked ?? 0;
129
+ }
130
+ async open(notification) {
131
+ const url = notification.url ?? await this.loadUrl(notification.id);
132
+ if (!notification.isRead) try {
133
+ await this.markRead([notification.id]);
134
+ } catch {}
135
+ this.navigate(url);
136
+ }
137
+ async enablePush() {
138
+ const permissions = this.permissions;
139
+ const swPush = this.swPush;
140
+ if (!this.pushSupported || !permissions || !swPush) return false;
141
+ const asked = permissions.requestPermission();
142
+ this.busy.set(true);
143
+ try {
144
+ const key = this.loadPublicKey();
145
+ const permission = await asked;
146
+ this.permission.set(permission);
147
+ if (permission !== "granted") return false;
148
+ const serverPublicKey = await key;
149
+ if (!serverPublicKey) return false;
150
+ const subscription = await swPush.requestSubscription({ serverPublicKey });
151
+ try {
152
+ await firstValueFrom(this.http.post(`${this.baseUrl}/push/subscribe`, subscription.toJSON()));
153
+ } catch {
154
+ await swPush.unsubscribe().catch(() => void 0);
155
+ this.subscribed.set(false);
156
+ return false;
157
+ }
158
+ this.subscribed.set(true);
159
+ return true;
160
+ } catch {
161
+ return false;
162
+ } finally {
163
+ this.busy.set(false);
164
+ }
165
+ }
166
+ async disablePush() {
167
+ const swPush = this.swPush;
168
+ if (!swPush?.isEnabled) return;
169
+ this.busy.set(true);
170
+ try {
171
+ const current = await firstValueFrom(swPush.subscription);
172
+ if (current) {
173
+ await firstValueFrom(this.http.post(`${this.baseUrl}/push/unsubscribe`, { endpoint: current.endpoint })).catch(() => void 0);
174
+ await swPush.unsubscribe().catch(() => void 0);
175
+ }
176
+ this.subscribed.set(false);
177
+ } finally {
178
+ this.busy.set(false);
179
+ }
180
+ }
181
+ follow(request, target) {
182
+ return this.live.watchQuery(request).subscribe((event) => target.set(event.type === "result" ? event.result : null));
183
+ }
184
+ loadPublicKey() {
185
+ this.publicKey ??= firstValueFrom(this.http.get(`${this.baseUrl}/push/key`)).then((response) => {
186
+ const key = response?.publicKey || null;
187
+ this.configured.set(!!key);
188
+ return key;
189
+ }, (error) => {
190
+ this.configured.set(false);
191
+ if (!(error instanceof HttpErrorResponse) || error.status === 0) this.publicKey = null;
192
+ return null;
193
+ });
194
+ return this.publicKey;
195
+ }
196
+ async loadUrl(id) {
197
+ try {
198
+ const value = (await this.spark.get(SPARK_NOTIFICATION_TYPE_ID, id))?.attributes?.find((a) => a.name === "Url")?.value;
199
+ return typeof value === "string" && value ? value : null;
200
+ } catch {
201
+ return null;
202
+ }
203
+ }
204
+ navigate(url) {
205
+ if (!url) return;
206
+ let parsed;
207
+ try {
208
+ parsed = new URL(url, this.document.baseURI);
209
+ } catch {
210
+ return;
211
+ }
212
+ const inApp = sparkInAppUrl(url, this.document, this.location);
213
+ if (inApp && this.router) {
214
+ this.router.navigateByUrl(inApp).catch(() => this.assign(parsed.href));
215
+ return;
216
+ }
217
+ if (parsed.protocol === "http:" || parsed.protocol === "https:") this.assign(parsed.href);
218
+ }
219
+ assign(href) {
220
+ this.document.defaultView?.location.assign(href);
221
+ }
222
+ static ɵfac = i0.ɵɵngDeclareFactory({
223
+ minVersion: "12.0.0",
224
+ version: "22.2.2",
225
+ ngImport: i0,
226
+ type: SparkNotificationsService,
227
+ deps: [],
228
+ target: i0.ɵɵFactoryTarget.Injectable
229
+ });
230
+ static ɵprov = i0.ɵɵngDeclareInjectable({
231
+ minVersion: "12.0.0",
232
+ version: "22.2.2",
233
+ ngImport: i0,
234
+ type: SparkNotificationsService,
235
+ providedIn: "root"
236
+ });
237
+ };
238
+ i0.ɵɵngDeclareClassMetadata({
239
+ minVersion: "12.0.0",
240
+ version: "22.2.2",
241
+ ngImport: i0,
242
+ type: SparkNotificationsService,
243
+ decorators: [{
244
+ type: Injectable,
245
+ args: [{ providedIn: "root" }]
246
+ }],
247
+ ctorParameters: () => []
248
+ });
249
+ const SPARK_NOTIFICATION_TEXTS = {
250
+ title: "Notifications",
251
+ empty: "No notifications",
252
+ markAllRead: "Mark all as read",
253
+ showAll: "All notifications",
254
+ pushEnable: "Turn on push notifications",
255
+ pushDisable: "Turn off push notifications on this device",
256
+ pushDenied: "Notifications are blocked for this site in your browser settings.",
257
+ pushUnsupported: "This browser cannot receive push notifications here. On iPhone and iPad, add the app to your Home Screen first."
258
+ };
259
+ var SparkPushToggleComponent = class SparkPushToggleComponent {
260
+ notifications = inject(SparkNotificationsService);
261
+ language = inject(SparkLanguageService);
262
+ t(key) {
263
+ return this.language.t("notifications.spa." + key, SPARK_NOTIFICATION_TEXTS[key]);
264
+ }
265
+ enable() {
266
+ this.notifications.enablePush();
267
+ }
268
+ disable() {
269
+ this.notifications.disablePush();
270
+ }
271
+ static ɵfac = i0.ɵɵngDeclareFactory({
272
+ minVersion: "12.0.0",
273
+ version: "22.2.2",
274
+ ngImport: i0,
275
+ type: SparkPushToggleComponent,
276
+ deps: [],
277
+ target: i0.ɵɵFactoryTarget.Component
278
+ });
279
+ static ɵcmp = i0.ɵɵngDeclareComponent({
280
+ minVersion: "17.0.0",
281
+ version: "22.2.2",
282
+ type: SparkPushToggleComponent,
283
+ isStandalone: true,
284
+ selector: "spark-push-toggle",
285
+ ngImport: i0,
286
+ template: `
287
+ @if (notifications.pushConfigured()) {
288
+ @switch (notifications.pushState()) {
289
+ @case ('unsupported') {
290
+ <p class="spark-push-unsupported small text-body-secondary mb-0">{{ t('pushUnsupported') }}</p>
291
+ }
292
+ @case ('denied') {
293
+ <p class="spark-push-denied small text-body-secondary mb-0">{{ t('pushDenied') }}</p>
294
+ }
295
+ @case ('on') {
296
+ <button type="button" class="btn btn-sm btn-outline-secondary spark-push-disable"
297
+ [disabled]="notifications.pushBusy()" (click)="disable()">{{ t('pushDisable') }}</button>
298
+ }
299
+ @default {
300
+ <button type="button" class="btn btn-sm btn-outline-primary spark-push-enable"
301
+ [disabled]="notifications.pushBusy()" (click)="enable()">{{ t('pushEnable') }}</button>
302
+ }
303
+ }
304
+ }
305
+ `,
306
+ isInline: true,
307
+ changeDetection: i0.ChangeDetectionStrategy.OnPush
308
+ });
309
+ };
310
+ i0.ɵɵngDeclareClassMetadata({
311
+ minVersion: "12.0.0",
312
+ version: "22.2.2",
313
+ ngImport: i0,
314
+ type: SparkPushToggleComponent,
315
+ decorators: [{
316
+ type: Component,
317
+ args: [{
318
+ selector: "spark-push-toggle",
319
+ standalone: true,
320
+ changeDetection: ChangeDetectionStrategy.OnPush,
321
+ template: `
322
+ @if (notifications.pushConfigured()) {
323
+ @switch (notifications.pushState()) {
324
+ @case ('unsupported') {
325
+ <p class="spark-push-unsupported small text-body-secondary mb-0">{{ t('pushUnsupported') }}</p>
326
+ }
327
+ @case ('denied') {
328
+ <p class="spark-push-denied small text-body-secondary mb-0">{{ t('pushDenied') }}</p>
329
+ }
330
+ @case ('on') {
331
+ <button type="button" class="btn btn-sm btn-outline-secondary spark-push-disable"
332
+ [disabled]="notifications.pushBusy()" (click)="disable()">{{ t('pushDisable') }}</button>
333
+ }
334
+ @default {
335
+ <button type="button" class="btn btn-sm btn-outline-primary spark-push-enable"
336
+ [disabled]="notifications.pushBusy()" (click)="enable()">{{ t('pushEnable') }}</button>
337
+ }
338
+ }
339
+ }
340
+ `
341
+ }]
342
+ }]
343
+ });
344
+ const UNITS = [
345
+ ["year", 31536e3],
346
+ ["month", 2592e3],
347
+ ["week", 604800],
348
+ ["day", 86400],
349
+ ["hour", 3600],
350
+ ["minute", 60]
351
+ ];
352
+ function sparkRelativeTime(date, now, language) {
353
+ if (!date) return "";
354
+ const seconds = Math.round((date.getTime() - now) / 1e3);
355
+ let format;
356
+ try {
357
+ format = new Intl.RelativeTimeFormat(language || "en", { numeric: "auto" });
358
+ } catch {
359
+ format = new Intl.RelativeTimeFormat("en", { numeric: "auto" });
360
+ }
361
+ for (const [unit, size] of UNITS) if (Math.abs(seconds) >= size) return format.format(Math.trunc(seconds / size), unit);
362
+ return format.format(0, "second");
363
+ }
364
+ var SparkNotificationBellComponent = class SparkNotificationBellComponent {
365
+ notifications = inject(SparkNotificationsService);
366
+ language = inject(SparkLanguageService);
367
+ options = inject(SPARK_NOTIFICATIONS_OPTIONS);
368
+ colors = Color;
369
+ allLink = input(null, ...ngDevMode ? [{ debugName: "allLink" }] : /* istanbul ignore next */ []);
370
+ open = signal(false, ...ngDevMode ? [{ debugName: "open" }] : /* istanbul ignore next */ []);
371
+ listLink = computed(() => this.allLink() ?? this.options.listLink, ...ngDevMode ? [{ debugName: "listLink" }] : /* istanbul ignore next */ []);
372
+ countText = computed(() => {
373
+ const count = this.notifications.unreadCount();
374
+ return count > 99 ? "99+" : String(count);
375
+ }, ...ngDevMode ? [{ debugName: "countText" }] : /* istanbul ignore next */ []);
376
+ ariaLabel = computed(() => {
377
+ const count = this.notifications.unreadCount();
378
+ return count > 0 ? `${this.t("title")} (${count})` : this.t("title");
379
+ }, ...ngDevMode ? [{ debugName: "ariaLabel" }] : /* istanbul ignore next */ []);
380
+ positions = [{
381
+ originX: "end",
382
+ originY: "bottom",
383
+ overlayX: "end",
384
+ overlayY: "top"
385
+ }, {
386
+ originX: "end",
387
+ originY: "top",
388
+ overlayX: "end",
389
+ overlayY: "bottom"
390
+ }];
391
+ t(key) {
392
+ return this.language.t("notifications.spa." + key, SPARK_NOTIFICATION_TEXTS[key]);
393
+ }
394
+ when(notification) {
395
+ return sparkRelativeTime(notification.createdAt, Date.now(), this.language.language());
396
+ }
397
+ close() {
398
+ this.open.set(false);
399
+ }
400
+ onOutsideClick(event, toggle) {
401
+ if (event.target instanceof Node && toggle.contains(event.target)) return;
402
+ this.close();
403
+ }
404
+ async choose(notification) {
405
+ this.close();
406
+ await this.notifications.open(notification);
407
+ }
408
+ async markAllRead() {
409
+ await this.notifications.markRead();
410
+ }
411
+ static ɵfac = i0.ɵɵngDeclareFactory({
412
+ minVersion: "12.0.0",
413
+ version: "22.2.2",
414
+ ngImport: i0,
415
+ type: SparkNotificationBellComponent,
416
+ deps: [],
417
+ target: i0.ɵɵFactoryTarget.Component
418
+ });
419
+ static ɵcmp = i0.ɵɵngDeclareComponent({
420
+ minVersion: "17.0.0",
421
+ version: "22.2.2",
422
+ type: SparkNotificationBellComponent,
423
+ isStandalone: true,
424
+ selector: "spark-notification-bell",
425
+ inputs: { allLink: {
426
+ classPropertyName: "allLink",
427
+ publicName: "allLink",
428
+ isSignal: true,
429
+ isRequired: false,
430
+ transformFunction: null
431
+ } },
432
+ ngImport: i0,
433
+ template: `
434
+ <button type="button" cdkOverlayOrigin #origin="cdkOverlayOrigin" #toggle
435
+ class="btn btn-link position-relative spark-notification-bell-toggle"
436
+ aria-haspopup="menu" [attr.aria-expanded]="open()" [attr.aria-label]="ariaLabel()"
437
+ (click)="open.set(!open())">
438
+ <spark-icon name="bell" />
439
+ @if (notifications.unreadCount() > 0) {
440
+ <bs-badge [type]="colors.danger" [decorative]="true"
441
+ class="position-absolute top-0 start-100 translate-middle spark-notification-count">{{ countText() }}</bs-badge>
442
+ }
443
+ </button>
444
+ <ng-template cdkConnectedOverlay
445
+ [cdkConnectedOverlayOrigin]="origin"
446
+ [cdkConnectedOverlayOpen]="open()"
447
+ [cdkConnectedOverlayPositions]="positions"
448
+ (overlayOutsideClick)="onOutsideClick($event, toggle)"
449
+ (overlayKeydown)="$event.key === 'Escape' && close()"
450
+ (detach)="close()">
451
+ <bs-dropdown-menu class="spark-notification-menu">
452
+ <li bsDropdownHeader class="d-flex align-items-center justify-content-between gap-3">
453
+ <span>{{ t('title') }}</span>
454
+ @if (notifications.unreadCount() > 0) {
455
+ <button type="button" class="btn btn-link btn-sm p-0 spark-notification-mark-all" (click)="markAllRead()">{{ t('markAllRead') }}</button>
456
+ }
457
+ </li>
458
+ @for (notification of notifications.recent(); track notification.id) {
459
+ <li bsDropdownItem class="spark-notification" [class.fw-semibold]="!notification.isRead"
460
+ [class.spark-notification-unread]="!notification.isRead" [attr.data-id]="notification.id"
461
+ (click)="choose(notification)">
462
+ <div class="spark-notification-title text-wrap">{{ notification.title }}</div>
463
+ @if (notification.body) {
464
+ <div class="spark-notification-body small fw-normal text-body-secondary text-wrap">{{ notification.body }}</div>
465
+ }
466
+ <div class="spark-notification-time small fw-normal text-body-secondary">{{ when(notification) }}</div>
467
+ </li>
468
+ } @empty {
469
+ <li class="dropdown-item-text text-body-secondary spark-notification-empty">{{ t('empty') }}</li>
470
+ }
471
+ <li bsDropdownDivider></li>
472
+ <li bsDropdownItem class="spark-notification-all">
473
+ <a [routerLink]="listLink()" (click)="close()">{{ t('showAll') }}</a>
474
+ </li>
475
+ @if (notifications.pushConfigured() && notifications.pushState() !== 'unsupported') {
476
+ <li class="dropdown-item-text spark-notification-push"><spark-push-toggle /></li>
477
+ }
478
+ </bs-dropdown-menu>
479
+ </ng-template>
480
+ `,
481
+ isInline: true,
482
+ styles: [":host{display:inline-block}.spark-notification-count{font-size:.65rem}\n"],
483
+ dependencies: [
484
+ {
485
+ kind: "directive",
486
+ type: RouterLink,
487
+ selector: "[routerLink]",
488
+ inputs: [
489
+ "target",
490
+ "queryParams",
491
+ "fragment",
492
+ "queryParamsHandling",
493
+ "state",
494
+ "info",
495
+ "relativeTo",
496
+ "preserveFragment",
497
+ "skipLocationChange",
498
+ "replaceUrl",
499
+ "browserUrl",
500
+ "routerLink"
501
+ ]
502
+ },
503
+ {
504
+ kind: "directive",
505
+ type: CdkOverlayOrigin,
506
+ selector: "[cdk-overlay-origin], [overlay-origin], [cdkOverlayOrigin]",
507
+ exportAs: ["cdkOverlayOrigin"]
508
+ },
509
+ {
510
+ kind: "directive",
511
+ type: CdkConnectedOverlay,
512
+ selector: "[cdk-connected-overlay], [connected-overlay], [cdkConnectedOverlay]",
513
+ inputs: [
514
+ "cdkConnectedOverlayOrigin",
515
+ "cdkConnectedOverlayPositions",
516
+ "cdkConnectedOverlayPositionStrategy",
517
+ "cdkConnectedOverlayOffsetX",
518
+ "cdkConnectedOverlayOffsetY",
519
+ "cdkConnectedOverlayWidth",
520
+ "cdkConnectedOverlayHeight",
521
+ "cdkConnectedOverlayMinWidth",
522
+ "cdkConnectedOverlayMinHeight",
523
+ "cdkConnectedOverlayBackdropClass",
524
+ "cdkConnectedOverlayPanelClass",
525
+ "cdkConnectedOverlayViewportMargin",
526
+ "cdkConnectedOverlayScrollStrategy",
527
+ "cdkConnectedOverlayOpen",
528
+ "cdkConnectedOverlayDisableClose",
529
+ "cdkConnectedOverlayTransformOriginOn",
530
+ "cdkConnectedOverlayHasBackdrop",
531
+ "cdkConnectedOverlayLockPosition",
532
+ "cdkConnectedOverlayFlexibleDimensions",
533
+ "cdkConnectedOverlayGrowAfterOpen",
534
+ "cdkConnectedOverlayPush",
535
+ "cdkConnectedOverlayDisposeOnNavigation",
536
+ "cdkConnectedOverlayUsePopover",
537
+ "cdkConnectedOverlayMatchWidth",
538
+ "cdkConnectedOverlay"
539
+ ],
540
+ outputs: [
541
+ "backdropClick",
542
+ "positionChange",
543
+ "attach",
544
+ "detach",
545
+ "overlayKeydown",
546
+ "overlayOutsideClick"
547
+ ],
548
+ exportAs: ["cdkConnectedOverlay"]
549
+ },
550
+ {
551
+ kind: "component",
552
+ type: BsBadgeComponent,
553
+ selector: "bs-badge",
554
+ inputs: [
555
+ "type",
556
+ "unit",
557
+ "decorative"
558
+ ]
559
+ },
560
+ {
561
+ kind: "component",
562
+ type: BsDropdownMenuComponent,
563
+ selector: "bs-dropdown-menu",
564
+ inputs: ["menuMode", "maxHeight"],
565
+ outputs: ["select"]
566
+ },
567
+ {
568
+ kind: "directive",
569
+ type: BsDropdownItemDirective,
570
+ selector: "[bsDropdownItem]",
571
+ inputs: [
572
+ "active",
573
+ "disabled",
574
+ "value"
575
+ ]
576
+ },
577
+ {
578
+ kind: "directive",
579
+ type: BsDropdownHeaderDirective,
580
+ selector: "[bsDropdownHeader]"
581
+ },
582
+ {
583
+ kind: "directive",
584
+ type: BsDropdownDividerDirective,
585
+ selector: "[bsDropdownDivider]"
586
+ },
587
+ {
588
+ kind: "component",
589
+ type: SparkIconComponent,
590
+ selector: "spark-icon",
591
+ inputs: ["name"]
592
+ },
593
+ {
594
+ kind: "component",
595
+ type: SparkPushToggleComponent,
596
+ selector: "spark-push-toggle"
597
+ }
598
+ ],
599
+ changeDetection: i0.ChangeDetectionStrategy.OnPush
600
+ });
601
+ };
602
+ i0.ɵɵngDeclareClassMetadata({
603
+ minVersion: "12.0.0",
604
+ version: "22.2.2",
605
+ ngImport: i0,
606
+ type: SparkNotificationBellComponent,
607
+ decorators: [{
608
+ type: Component,
609
+ args: [{
610
+ selector: "spark-notification-bell",
611
+ standalone: true,
612
+ changeDetection: ChangeDetectionStrategy.OnPush,
613
+ imports: [
614
+ RouterLink,
615
+ CdkOverlayOrigin,
616
+ CdkConnectedOverlay,
617
+ BsBadgeComponent,
618
+ BsDropdownMenuComponent,
619
+ BsDropdownItemDirective,
620
+ BsDropdownHeaderDirective,
621
+ BsDropdownDividerDirective,
622
+ SparkIconComponent,
623
+ SparkPushToggleComponent
624
+ ],
625
+ template: `
626
+ <button type="button" cdkOverlayOrigin #origin="cdkOverlayOrigin" #toggle
627
+ class="btn btn-link position-relative spark-notification-bell-toggle"
628
+ aria-haspopup="menu" [attr.aria-expanded]="open()" [attr.aria-label]="ariaLabel()"
629
+ (click)="open.set(!open())">
630
+ <spark-icon name="bell" />
631
+ @if (notifications.unreadCount() > 0) {
632
+ <bs-badge [type]="colors.danger" [decorative]="true"
633
+ class="position-absolute top-0 start-100 translate-middle spark-notification-count">{{ countText() }}</bs-badge>
634
+ }
635
+ </button>
636
+ <ng-template cdkConnectedOverlay
637
+ [cdkConnectedOverlayOrigin]="origin"
638
+ [cdkConnectedOverlayOpen]="open()"
639
+ [cdkConnectedOverlayPositions]="positions"
640
+ (overlayOutsideClick)="onOutsideClick($event, toggle)"
641
+ (overlayKeydown)="$event.key === 'Escape' && close()"
642
+ (detach)="close()">
643
+ <bs-dropdown-menu class="spark-notification-menu">
644
+ <li bsDropdownHeader class="d-flex align-items-center justify-content-between gap-3">
645
+ <span>{{ t('title') }}</span>
646
+ @if (notifications.unreadCount() > 0) {
647
+ <button type="button" class="btn btn-link btn-sm p-0 spark-notification-mark-all" (click)="markAllRead()">{{ t('markAllRead') }}</button>
648
+ }
649
+ </li>
650
+ @for (notification of notifications.recent(); track notification.id) {
651
+ <li bsDropdownItem class="spark-notification" [class.fw-semibold]="!notification.isRead"
652
+ [class.spark-notification-unread]="!notification.isRead" [attr.data-id]="notification.id"
653
+ (click)="choose(notification)">
654
+ <div class="spark-notification-title text-wrap">{{ notification.title }}</div>
655
+ @if (notification.body) {
656
+ <div class="spark-notification-body small fw-normal text-body-secondary text-wrap">{{ notification.body }}</div>
657
+ }
658
+ <div class="spark-notification-time small fw-normal text-body-secondary">{{ when(notification) }}</div>
659
+ </li>
660
+ } @empty {
661
+ <li class="dropdown-item-text text-body-secondary spark-notification-empty">{{ t('empty') }}</li>
662
+ }
663
+ <li bsDropdownDivider></li>
664
+ <li bsDropdownItem class="spark-notification-all">
665
+ <a [routerLink]="listLink()" (click)="close()">{{ t('showAll') }}</a>
666
+ </li>
667
+ @if (notifications.pushConfigured() && notifications.pushState() !== 'unsupported') {
668
+ <li class="dropdown-item-text spark-notification-push"><spark-push-toggle /></li>
669
+ }
670
+ </bs-dropdown-menu>
671
+ </ng-template>
672
+ `,
673
+ styles: [":host{display:inline-block}.spark-notification-count{font-size:.65rem}\n"]
674
+ }]
675
+ }],
676
+ propDecorators: { allLink: [{
677
+ type: i0.Input,
678
+ args: [{
679
+ isSignal: true,
680
+ alias: "allLink",
681
+ required: false
682
+ }]
683
+ }] }
684
+ });
685
+ export { SPARK_MY_NOTIFICATIONS_QUERY_ID, SPARK_NOTIFICATIONS_OPTIONS, SPARK_NOTIFICATION_TEXTS, SPARK_NOTIFICATION_TYPE_ID, SPARK_PUSH_PERMISSIONS, SPARK_UNREAD_NOTIFICATIONS_QUERY_ID, SparkNotificationBellComponent, SparkNotificationsService, SparkPushToggleComponent, provideSparkNotifications, sparkRelativeTime, toSparkNotification };
686
+
687
+ //# sourceMappingURL=mintplayer-ng-spark-notifications.mjs.map