@equinor/fusion-framework-module-bookmark 2.2.0 → 3.0.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.
- package/CHANGELOG.md +84 -0
- package/dist/esm/BookmarkConfigurator.js +6 -3
- package/dist/esm/BookmarkConfigurator.js.map +1 -1
- package/dist/esm/BookmarkProvider.js +287 -219
- package/dist/esm/BookmarkProvider.js.map +1 -1
- package/dist/esm/bookmark-config.schema.js +5 -4
- package/dist/esm/bookmark-config.schema.js.map +1 -1
- package/dist/esm/bookmark.schemas.js +4 -1
- package/dist/esm/bookmark.schemas.js.map +1 -1
- package/dist/esm/version.js +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types/BookmarkProvider.actions.d.ts +7 -175
- package/dist/types/BookmarkProvider.d.ts +47 -46
- package/dist/types/BookmarkProvider.reducer.d.ts +7 -90
- package/dist/types/bookmark-config.schema.d.ts +13 -104
- package/dist/types/bookmark.schemas.d.ts +20 -376
- package/dist/types/types.d.ts +96 -18
- package/dist/types/version.d.ts +1 -1
- package/package.json +12 -12
- package/src/BookmarkConfigurator.ts +10 -5
- package/src/BookmarkProvider.ts +377 -307
- package/src/bookmark-config.schema.ts +7 -6
- package/src/bookmark.schemas.ts +6 -2
- package/src/types.ts +92 -28
- package/src/version.ts +1 -1
package/src/BookmarkProvider.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Observable, Subscription, forkJoin, from, lastValueFrom, of } from 'rxjs';
|
|
1
|
+
import { Observable, Subscription, forkJoin, from, lastValueFrom, of, defer } from 'rxjs';
|
|
2
2
|
|
|
3
3
|
import {
|
|
4
4
|
filter,
|
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
catchError,
|
|
13
13
|
} from 'rxjs/operators';
|
|
14
14
|
|
|
15
|
-
import {
|
|
15
|
+
import { castDraft, createDraft, finishDraft } from 'immer';
|
|
16
16
|
|
|
17
17
|
import { v4 as generateGUID } from 'uuid';
|
|
18
18
|
|
|
@@ -61,7 +61,8 @@ import type {
|
|
|
61
61
|
IBookmarkProvider,
|
|
62
62
|
} from './BookmarkProvider.interface';
|
|
63
63
|
|
|
64
|
-
|
|
64
|
+
// Default timeout for bookmark operations (2 minutes)
|
|
65
|
+
const defaultTimeout = 2 * 60 * 1000;
|
|
65
66
|
|
|
66
67
|
/**
|
|
67
68
|
* The `BookmarkProvider` class is responsible for managing bookmarks in the application.
|
|
@@ -356,9 +357,11 @@ export class BookmarkProvider implements IBookmarkProvider {
|
|
|
356
357
|
): VoidFunction {
|
|
357
358
|
if (!this._event) {
|
|
358
359
|
this._log?.warn('Failed to register event listener, event provider not configured');
|
|
359
|
-
return () => {
|
|
360
|
+
return () => {
|
|
361
|
+
// No-op function when event provider is not configured
|
|
362
|
+
};
|
|
360
363
|
}
|
|
361
|
-
return this._event
|
|
364
|
+
return this._event.addEventListener(eventName, (event) => {
|
|
362
365
|
if (event.source === this) {
|
|
363
366
|
callback(event);
|
|
364
367
|
}
|
|
@@ -398,17 +401,21 @@ export class BookmarkProvider implements IBookmarkProvider {
|
|
|
398
401
|
* @returns An observable that emits the generated payload.
|
|
399
402
|
* @template T - The type of the generated payload.
|
|
400
403
|
*/
|
|
401
|
-
public generatePayload<T extends BookmarkData>(
|
|
404
|
+
public generatePayload<T extends BookmarkData = BookmarkData>(
|
|
402
405
|
initial?: Partial<T> | null,
|
|
403
406
|
): Observable<T | null | undefined> {
|
|
404
|
-
this._log?.debug(
|
|
407
|
+
this._log?.debug('generating payload', initial);
|
|
405
408
|
|
|
406
409
|
/**
|
|
407
410
|
* Observable that emits the generated payload.
|
|
408
411
|
*/
|
|
409
412
|
const result$ = from(this.#payloadGenerators).pipe(
|
|
410
|
-
mergeScan(
|
|
411
|
-
|
|
413
|
+
mergeScan(
|
|
414
|
+
(acc, generator) => from(Promise.resolve(this._producePayload(acc, generator))),
|
|
415
|
+
initial ?? ({} as Partial<T>),
|
|
416
|
+
1,
|
|
417
|
+
),
|
|
418
|
+
tap((payload) => this._log?.debug('generated payload', { initial, payload })),
|
|
412
419
|
) as Observable<T | null | undefined>;
|
|
413
420
|
|
|
414
421
|
return result$;
|
|
@@ -428,35 +435,41 @@ export class BookmarkProvider implements IBookmarkProvider {
|
|
|
428
435
|
* If the generator returns a value, a warning will be logged.
|
|
429
436
|
* If the generator returns null, an info message will be logged indicating that the bookmark data should be cleared.
|
|
430
437
|
*/
|
|
431
|
-
protected _producePayload<T extends BookmarkData>(
|
|
438
|
+
protected async _producePayload<T extends BookmarkData = BookmarkData>(
|
|
432
439
|
value: Partial<T>,
|
|
433
440
|
generator: BookmarkPayloadGenerator<T>,
|
|
434
441
|
initial?: Partial<T> | null,
|
|
435
|
-
) {
|
|
436
|
-
//
|
|
437
|
-
|
|
438
|
-
try {
|
|
439
|
-
const result = await Promise.resolve(generator(draft as Partial<T>, initial));
|
|
440
|
-
// cast the result to a draft object
|
|
441
|
-
if (result) {
|
|
442
|
-
this._log?.warn(
|
|
443
|
-
`bookmark data generator ${generator.name} returned a value, but it should not do this, since the data is an immer draft object`,
|
|
444
|
-
);
|
|
445
|
-
// Dirty fix since some developers are returning the reference object, which will freeze the object
|
|
446
|
-
return castDraft(JSON.parse(JSON.stringify(result)));
|
|
447
|
-
}
|
|
442
|
+
): Promise<T | null | undefined> {
|
|
443
|
+
// Create a draft for async operations using createDraft/finishDraft pattern
|
|
444
|
+
const draft = createDraft(value);
|
|
448
445
|
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
446
|
+
try {
|
|
447
|
+
const generatorResult = await Promise.resolve(generator(draft as Partial<T>, initial));
|
|
448
|
+
|
|
449
|
+
// Handle the generator result
|
|
450
|
+
if (generatorResult) {
|
|
451
|
+
this._log?.warn(
|
|
452
|
+
`bookmark data generator ${generator.name} returned a value, but it should not do this, since the data is an immer draft object`,
|
|
453
|
+
);
|
|
454
|
+
// Dirty fix since some developers are returning the reference object, which will freeze the object
|
|
455
|
+
return castDraft(generatorResult) as T | null | undefined;
|
|
458
456
|
}
|
|
459
|
-
|
|
457
|
+
|
|
458
|
+
// clear the bookmark data if the generator returns null
|
|
459
|
+
if (generatorResult === null) {
|
|
460
|
+
this._log?.info(
|
|
461
|
+
`bookmark data generator ${generator.name} wish to clear the bookmark data`,
|
|
462
|
+
);
|
|
463
|
+
return undefined;
|
|
464
|
+
}
|
|
465
|
+
} catch (error) {
|
|
466
|
+
this._log?.error(`Failed to produce payload using generator ${generator.name}`, error);
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
// Finish the draft to get the immutable result
|
|
470
|
+
const result = finishDraft(draft);
|
|
471
|
+
|
|
472
|
+
return result as T | null | undefined;
|
|
460
473
|
}
|
|
461
474
|
|
|
462
475
|
/**
|
|
@@ -466,10 +479,10 @@ export class BookmarkProvider implements IBookmarkProvider {
|
|
|
466
479
|
* @param options An optional object that allows excluding the bookmark payload from the result.
|
|
467
480
|
* @returns An observable that emits the combined result of fetching the bookmark and bookmark data.
|
|
468
481
|
*/
|
|
469
|
-
public getBookmark<T extends BookmarkData>(
|
|
482
|
+
public getBookmark<T extends BookmarkData = BookmarkData>(
|
|
470
483
|
bookmarkId: string,
|
|
471
484
|
options?: { excludePayload?: boolean },
|
|
472
|
-
) {
|
|
485
|
+
): Observable<Bookmark<T>> {
|
|
473
486
|
this._log?.debug(`fetching bookmark: ${bookmarkId}`, options);
|
|
474
487
|
|
|
475
488
|
// fetch the bookmark and bookmark data
|
|
@@ -497,7 +510,7 @@ export class BookmarkProvider implements IBookmarkProvider {
|
|
|
497
510
|
* @param options.excludePayload - Specifies whether to exclude the payload from the bookmark.
|
|
498
511
|
* @returns A promise that resolves to the retrieved bookmark, or null if the bookmark is not found.
|
|
499
512
|
*/
|
|
500
|
-
public getBookmarkAsync<T extends BookmarkData>(
|
|
513
|
+
public getBookmarkAsync<T extends BookmarkData = BookmarkData>(
|
|
501
514
|
id: string,
|
|
502
515
|
options?: { excludePayload?: boolean },
|
|
503
516
|
): Promise<Bookmark<T> | null> {
|
|
@@ -505,24 +518,59 @@ export class BookmarkProvider implements IBookmarkProvider {
|
|
|
505
518
|
}
|
|
506
519
|
|
|
507
520
|
/**
|
|
508
|
-
*
|
|
521
|
+
* Retrieves all bookmarks from the store as an Observable stream.
|
|
509
522
|
*
|
|
510
|
-
*
|
|
523
|
+
* This method implements a complex flow that:
|
|
524
|
+
* 1. Resolves filter parameters (sourceSystem, appKey, contextId) based on configuration
|
|
525
|
+
* 2. Dispatches a fetch action to the store with resolved filters
|
|
526
|
+
* 3. Monitors store actions for success/failure responses
|
|
527
|
+
* 4. Returns the bookmarks data or throws appropriate errors
|
|
528
|
+
*
|
|
529
|
+
* @remarks
|
|
530
|
+
* The method uses a reactive pattern where filter resolution and store actions are
|
|
531
|
+
* handled asynchronously. If filtering is enabled, it will resolve the current
|
|
532
|
+
* application and context to filter bookmarks accordingly.
|
|
533
|
+
*
|
|
534
|
+
* @example
|
|
535
|
+
* ```ts
|
|
536
|
+
* // Basic usage
|
|
537
|
+
* bookmarkProvider.getAllBookmarks().subscribe({
|
|
538
|
+
* next: (bookmarks) => console.log('Retrieved bookmarks:', bookmarks),
|
|
539
|
+
* error: (err) => console.error('Failed to fetch bookmarks:', err)
|
|
540
|
+
* });
|
|
541
|
+
*
|
|
542
|
+
* // With filtering enabled in config
|
|
543
|
+
* const config = {
|
|
544
|
+
* filters: { context: true, application: true }
|
|
545
|
+
* };
|
|
546
|
+
* // Will automatically filter by current context and application
|
|
547
|
+
* ```
|
|
548
|
+
*
|
|
549
|
+
* @returns An Observable that emits the array of bookmarks when successfully retrieved
|
|
550
|
+
* @throws {BookmarkProviderError} When filter resolution fails or store operations fail
|
|
551
|
+
* @throws {BookmarkProviderError} When a timeout occurs during the fetch operation
|
|
511
552
|
*/
|
|
512
553
|
public getAllBookmarks(): Observable<Bookmarks> {
|
|
513
554
|
return new Observable<Bookmarks>((observer) => {
|
|
514
|
-
this._log?.debug(
|
|
555
|
+
this._log?.debug('fetching all bookmarks');
|
|
515
556
|
|
|
516
|
-
//
|
|
557
|
+
// Step 1: Generate filter parameters based on configuration
|
|
558
|
+
// This creates a stream that resolves all necessary filter values
|
|
517
559
|
const filter$ = forkJoin({
|
|
560
|
+
// Always include the source system for the current provider
|
|
518
561
|
sourceSystem: of(this.sourceSystem),
|
|
562
|
+
|
|
563
|
+
// Resolve application key if filtering by application is enabled
|
|
519
564
|
appKey: this.#config.filters?.application
|
|
520
|
-
?
|
|
565
|
+
? defer(() => this._resolve.application()).pipe(map((x) => x?.appKey))
|
|
521
566
|
: of(undefined),
|
|
567
|
+
|
|
568
|
+
// Resolve context ID if filtering by context is enabled
|
|
522
569
|
contextId: this.#config.filters?.context
|
|
523
|
-
?
|
|
570
|
+
? defer(() => this._resolve.context()).pipe(map((x) => x?.id))
|
|
524
571
|
: of(undefined),
|
|
525
572
|
}).pipe(
|
|
573
|
+
// Handle any errors during filter parameter resolution
|
|
526
574
|
catchError((err) => {
|
|
527
575
|
const error = new BookmarkProviderError(
|
|
528
576
|
'Could not fetch bookmarks, failed to resolve filter parameters',
|
|
@@ -533,20 +581,25 @@ export class BookmarkProvider implements IBookmarkProvider {
|
|
|
533
581
|
}),
|
|
534
582
|
);
|
|
535
583
|
|
|
536
|
-
//
|
|
584
|
+
// Step 2: Dispatch fetch action to store when filter parameters are ready
|
|
585
|
+
// This triggers the actual API call through the store's action system
|
|
586
|
+
// The store will handle the async operation and emit success/failure actions
|
|
587
|
+
// Using observer.add() ensures proper cleanup when the Observable is unsubscribed
|
|
537
588
|
observer.add(
|
|
538
589
|
filter$.subscribe((filter) => {
|
|
539
590
|
this.#store.next(bookmarkActions.fetchBookmarks(filter));
|
|
540
591
|
}),
|
|
541
592
|
);
|
|
542
593
|
|
|
543
|
-
//
|
|
594
|
+
// Step 3: Monitor for failure responses from the store
|
|
595
|
+
// This stream will emit and throw an error if the fetch operation fails
|
|
544
596
|
const failure$ = this.#store.action$.pipe(
|
|
545
597
|
filter(bookmarkActions.fetchBookmarks.failure.match),
|
|
546
598
|
map((action) => {
|
|
547
599
|
this._log?.error('Failed to fetch bookmarks', action.payload);
|
|
548
600
|
throw new BookmarkProviderError('Failed to fetch bookmarks', action.payload);
|
|
549
601
|
}),
|
|
602
|
+
// Add timeout protection to prevent hanging requests
|
|
550
603
|
timeout({
|
|
551
604
|
each: defaultTimeout,
|
|
552
605
|
with: () => {
|
|
@@ -555,17 +608,22 @@ export class BookmarkProvider implements IBookmarkProvider {
|
|
|
555
608
|
}),
|
|
556
609
|
);
|
|
557
610
|
|
|
558
|
-
//
|
|
611
|
+
// Step 4: Monitor for success responses from the store
|
|
612
|
+
// This stream will emit the bookmarks data when successfully retrieved
|
|
559
613
|
const request$ = this.#store.action$.pipe(
|
|
560
614
|
filter(bookmarkActions.fetchBookmarks.success.match),
|
|
561
615
|
map((a) => a.payload),
|
|
562
616
|
tap((bookmarks) => {
|
|
563
|
-
this._log?.debug(
|
|
617
|
+
this._log?.debug('fetched all bookmarks', bookmarks);
|
|
564
618
|
}),
|
|
619
|
+
// Race against failure stream - whichever completes first wins
|
|
620
|
+
// This ensures we either get the bookmarks data or an error, not both
|
|
565
621
|
raceWith(failure$),
|
|
622
|
+
// Only take the first emission (success or failure) to complete the stream
|
|
566
623
|
first(),
|
|
567
624
|
);
|
|
568
625
|
|
|
626
|
+
// Step 5: Subscribe to the request stream and forward results to observer
|
|
569
627
|
request$.subscribe(observer);
|
|
570
628
|
});
|
|
571
629
|
}
|
|
@@ -590,7 +648,7 @@ export class BookmarkProvider implements IBookmarkProvider {
|
|
|
590
648
|
* @returns An observable that emits the next bookmark or null.
|
|
591
649
|
* @template T - The type of the bookmark data.
|
|
592
650
|
*/
|
|
593
|
-
public setCurrentBookmark<T extends BookmarkData>(
|
|
651
|
+
public setCurrentBookmark<T extends BookmarkData = BookmarkData>(
|
|
594
652
|
bookmark_or_id: Bookmark<T> | string | null,
|
|
595
653
|
): Observable<Bookmark<T> | null> {
|
|
596
654
|
this._log?.debug('setting current bookmark', bookmark_or_id);
|
|
@@ -642,7 +700,7 @@ export class BookmarkProvider implements IBookmarkProvider {
|
|
|
642
700
|
* @param bookmarkId - The ID of the bookmark to set as the current bookmark.
|
|
643
701
|
* @returns A subscription to the operation that sets the current bookmark.
|
|
644
702
|
*/
|
|
645
|
-
public setCurrentBookmarkAsync<T extends BookmarkData>(
|
|
703
|
+
public setCurrentBookmarkAsync<T extends BookmarkData = BookmarkData>(
|
|
646
704
|
bookmark_or_id: Bookmark<T> | string | null,
|
|
647
705
|
): Promise<Bookmark<T> | null> {
|
|
648
706
|
return lastValueFrom(this.setCurrentBookmark<T>(bookmark_or_id));
|
|
@@ -650,116 +708,121 @@ export class BookmarkProvider implements IBookmarkProvider {
|
|
|
650
708
|
|
|
651
709
|
/**
|
|
652
710
|
* Creates a new bookmark with the provided bookmark data.
|
|
711
|
+
* The creation executes immediately when this method is called.
|
|
653
712
|
* @template T - The type of bookmark data.
|
|
654
713
|
* @param {BookmarkCreateArgs<T>} newBookmarkData - The data for creating the bookmark.
|
|
655
714
|
* @returns {Observable<Bookmark<T>>} - An observable that emits the created bookmark.
|
|
656
715
|
*/
|
|
657
|
-
public createBookmark<T extends BookmarkData>(
|
|
716
|
+
public createBookmark<T extends BookmarkData = BookmarkData>(
|
|
658
717
|
newBookmarkData: BookmarkCreateArgs<T>,
|
|
659
718
|
): Observable<Bookmark<T>> {
|
|
660
|
-
|
|
661
|
-
|
|
719
|
+
const { ref, action$ } = this._useScopedActions();
|
|
720
|
+
|
|
721
|
+
this._log?.debug(`creating new bookmark, ref: ${ref}`, newBookmarkData);
|
|
722
|
+
|
|
723
|
+
// resolve the bookmark
|
|
724
|
+
const bookmark$ = forkJoin({
|
|
725
|
+
appKey: newBookmarkData.appKey
|
|
726
|
+
? of(newBookmarkData.appKey)
|
|
727
|
+
: defer(() => this._resolve.application()).pipe(
|
|
728
|
+
map((app) => {
|
|
729
|
+
if (!app?.appKey) {
|
|
730
|
+
throw new BookmarkProviderError('Failed to resolve application key');
|
|
731
|
+
}
|
|
732
|
+
return app.appKey;
|
|
733
|
+
}),
|
|
734
|
+
),
|
|
735
|
+
contextId: defer(() => this._resolve.context()).pipe(map((context) => context?.id)),
|
|
736
|
+
payload: this.generatePayload<T>(newBookmarkData.payload),
|
|
737
|
+
sourceSystem: of(this.sourceSystem),
|
|
738
|
+
}).pipe(
|
|
739
|
+
catchError((err) => {
|
|
740
|
+
const error = new BookmarkProviderError(
|
|
741
|
+
'Could not create new bookmark, failed to resolve bookmark data',
|
|
742
|
+
err,
|
|
743
|
+
);
|
|
744
|
+
this._log?.error(error.message, error);
|
|
745
|
+
throw error;
|
|
746
|
+
}),
|
|
747
|
+
// merge the resolved data with the new bookmark data
|
|
748
|
+
map(
|
|
749
|
+
(resolvedData) =>
|
|
750
|
+
({
|
|
751
|
+
...newBookmarkData,
|
|
752
|
+
...resolvedData,
|
|
753
|
+
}) as BookmarkNew<T>,
|
|
754
|
+
),
|
|
755
|
+
);
|
|
662
756
|
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
:
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
return app.appKey;
|
|
675
|
-
}),
|
|
676
|
-
),
|
|
677
|
-
contextId: this._resolve.context().then((context) => context?.id),
|
|
678
|
-
payload: this.generatePayload<T>(newBookmarkData.payload),
|
|
679
|
-
sourceSystem: of(this.sourceSystem),
|
|
680
|
-
}).pipe(
|
|
681
|
-
catchError((err) => {
|
|
757
|
+
// notify listeners that a bookmark is about to be created
|
|
758
|
+
const dispatch$ = bookmark$.pipe(
|
|
759
|
+
switchMap(async (bookmark) => {
|
|
760
|
+
// notify listeners that a bookmark is about to be created
|
|
761
|
+
const { canceled, type } = await this._dispatchEvent('onBookmarkCreate', {
|
|
762
|
+
detail: bookmark,
|
|
763
|
+
cancelable: true,
|
|
764
|
+
});
|
|
765
|
+
|
|
766
|
+
// throw an error if the event is canceled
|
|
767
|
+
if (canceled) {
|
|
682
768
|
const error = new BookmarkProviderError(
|
|
683
|
-
|
|
684
|
-
err,
|
|
769
|
+
`event: ${type} was canceled by listener for creating bookmark: ${ref}`,
|
|
685
770
|
);
|
|
686
|
-
this._log?.
|
|
771
|
+
this._log?.info(error.message);
|
|
687
772
|
throw error;
|
|
688
|
-
}
|
|
689
|
-
// merge the resolved data with the new bookmark data
|
|
690
|
-
map(
|
|
691
|
-
(resolvedData) =>
|
|
692
|
-
({
|
|
693
|
-
...newBookmarkData,
|
|
694
|
-
...resolvedData,
|
|
695
|
-
}) as BookmarkNew<T>,
|
|
696
|
-
),
|
|
697
|
-
);
|
|
698
|
-
|
|
699
|
-
// notify listeners that a bookmark is about to be created
|
|
700
|
-
const dispatch$ = bookmark$.pipe(
|
|
701
|
-
switchMap(async (bookmark) => {
|
|
702
|
-
// notify listeners that a bookmark is about to be created
|
|
703
|
-
const { canceled, type } = await this._dispatchEvent('onBookmarkCreate', {
|
|
704
|
-
detail: bookmark,
|
|
705
|
-
cancelable: true,
|
|
706
|
-
});
|
|
773
|
+
}
|
|
707
774
|
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
`event: ${type} was canceled by listener for creating bookmark: ${ref}`,
|
|
712
|
-
);
|
|
713
|
-
this._log?.info(error.message);
|
|
714
|
-
throw error;
|
|
715
|
-
}
|
|
775
|
+
return bookmark;
|
|
776
|
+
}),
|
|
777
|
+
);
|
|
716
778
|
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
)
|
|
779
|
+
// Start the creation process immediately
|
|
780
|
+
const createSubscription = dispatch$.subscribe({
|
|
781
|
+
error: (error) => {
|
|
782
|
+
this._log?.error(`Failed to prepare bookmark creation: ${ref}`, error);
|
|
783
|
+
},
|
|
784
|
+
next: (newBookmark) => {
|
|
785
|
+
// request the store to create the bookmark
|
|
786
|
+
this.#store.next(bookmarkActions.createBookmark(newBookmark, { ref }));
|
|
787
|
+
},
|
|
788
|
+
});
|
|
720
789
|
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
)
|
|
790
|
+
// monitor the failure case when creating a bookmark
|
|
791
|
+
const failure$ = action$.pipe(
|
|
792
|
+
filter(bookmarkActions.createBookmark.failure.match),
|
|
793
|
+
map(({ payload: cause }) => {
|
|
794
|
+
const error = new BookmarkProviderError(`Failed to create bookmark: ${ref}`, {
|
|
795
|
+
cause,
|
|
796
|
+
});
|
|
797
|
+
this._log?.warn(error.message);
|
|
798
|
+
throw error;
|
|
799
|
+
}),
|
|
800
|
+
timeout({
|
|
801
|
+
each: defaultTimeout,
|
|
802
|
+
with: () => {
|
|
803
|
+
throw new BookmarkProviderError(`Timeout while creating bookmark: ${ref}`);
|
|
804
|
+
},
|
|
805
|
+
}),
|
|
806
|
+
);
|
|
731
807
|
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
each: defaultTimeout,
|
|
744
|
-
with: () => {
|
|
745
|
-
throw new BookmarkProviderError(`Timeout while creating bookmark: ${ref}`);
|
|
746
|
-
},
|
|
747
|
-
}),
|
|
748
|
-
);
|
|
808
|
+
// monitor the success case when creating a bookmark
|
|
809
|
+
const request$ = action$.pipe(
|
|
810
|
+
filter(bookmarkActions.createBookmark.success.match),
|
|
811
|
+
map(({ payload }) => payload as Bookmark<T>),
|
|
812
|
+
tap((bookmark) => {
|
|
813
|
+
this._log?.info(`Bookmark created: ${bookmark.id}, ref: ${ref}`);
|
|
814
|
+
this._dispatchEvent('onBookmarkCreated', { detail: bookmark });
|
|
815
|
+
}),
|
|
816
|
+
raceWith(failure$),
|
|
817
|
+
first(),
|
|
818
|
+
);
|
|
749
819
|
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
tap((bookmark) => {
|
|
755
|
-
this._log?.info(`Bookmark created: ${bookmark.id}, ref: ${ref}`);
|
|
756
|
-
this._dispatchEvent('onBookmarkCreated', { detail: bookmark });
|
|
757
|
-
}),
|
|
758
|
-
raceWith(failure$),
|
|
759
|
-
first(),
|
|
760
|
-
);
|
|
820
|
+
// Return the observable that will emit the result
|
|
821
|
+
return new Observable<Bookmark<T>>((subscriber) => {
|
|
822
|
+
// Clean up the create subscription when the result observable is unsubscribed
|
|
823
|
+
subscriber.add(() => createSubscription.unsubscribe());
|
|
761
824
|
|
|
762
|
-
//
|
|
825
|
+
// Emit the created bookmark
|
|
763
826
|
request$.subscribe(subscriber);
|
|
764
827
|
});
|
|
765
828
|
}
|
|
@@ -770,7 +833,7 @@ export class BookmarkProvider implements IBookmarkProvider {
|
|
|
770
833
|
* @param bookmark - The new bookmark to create.
|
|
771
834
|
* @returns A promise that resolves to the created bookmark with its associated data.
|
|
772
835
|
*/
|
|
773
|
-
public createBookmarkAsync<T extends BookmarkData>(
|
|
836
|
+
public createBookmarkAsync<T extends BookmarkData = BookmarkData>(
|
|
774
837
|
args: BookmarkCreateArgs<T>,
|
|
775
838
|
): Promise<Bookmark<T>> {
|
|
776
839
|
return lastValueFrom(this.createBookmark<T>(args));
|
|
@@ -778,14 +841,15 @@ export class BookmarkProvider implements IBookmarkProvider {
|
|
|
778
841
|
|
|
779
842
|
/**
|
|
780
843
|
* Updates a bookmark with the specified bookmarkId and bookmarkUpdates.
|
|
844
|
+
* The update executes immediately when this method is called.
|
|
781
845
|
*
|
|
782
846
|
* @template T - The type of the bookmark data.
|
|
783
847
|
* @param {string} bookmarkId - The identifier of the bookmark to update.
|
|
784
848
|
* @param {BookmarkUpdate<T>} [bookmarkUpdates] - The updates to apply to the bookmark.
|
|
785
849
|
* @param {BookmarkUpdateOptions} [options] - The options for updating the bookmark.
|
|
786
|
-
* @returns {
|
|
850
|
+
* @returns {Observable<Bookmark<T>>} - An observable that emits the updated bookmark.
|
|
787
851
|
*/
|
|
788
|
-
public updateBookmark<T extends BookmarkData>(
|
|
852
|
+
public updateBookmark<T extends BookmarkData = BookmarkData>(
|
|
789
853
|
bookmarkId: string,
|
|
790
854
|
bookmarkUpdates?: BookmarkUpdate<T>,
|
|
791
855
|
options?: BookmarkUpdateOptions,
|
|
@@ -796,101 +860,105 @@ export class BookmarkProvider implements IBookmarkProvider {
|
|
|
796
860
|
);
|
|
797
861
|
}
|
|
798
862
|
|
|
799
|
-
|
|
800
|
-
const { ref, action$ } = this._useScopedActions();
|
|
863
|
+
const { ref, action$ } = this._useScopedActions();
|
|
801
864
|
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
/**
|
|
805
|
-
* Generate updates with payload
|
|
806
|
-
* @remarks
|
|
807
|
-
* If `excludePayloadGeneration` is `true`, it emits the `bookmarkUpdates` directly.
|
|
808
|
-
* If `excludePayloadGeneration` is `false`, it generates the payload using the `generatePayload` method and emits the updated `bookmarkUpdates` with the generated payload.
|
|
809
|
-
*/
|
|
810
|
-
const updates$ = options?.excludePayloadGeneration
|
|
811
|
-
? of(bookmarkUpdates as BookmarkUpdate<T>)
|
|
812
|
-
: this.generatePayload<T>(bookmarkUpdates?.payload).pipe(
|
|
813
|
-
map(
|
|
814
|
-
(payload) =>
|
|
815
|
-
({
|
|
816
|
-
...bookmarkUpdates,
|
|
817
|
-
payload,
|
|
818
|
-
}) as BookmarkUpdate<T>,
|
|
819
|
-
),
|
|
820
|
-
);
|
|
865
|
+
this._log?.debug(`Updating bookmark: ${bookmarkId}, ref: ${ref}`);
|
|
821
866
|
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
}
|
|
840
|
-
|
|
841
|
-
return updates;
|
|
842
|
-
}),
|
|
843
|
-
);
|
|
867
|
+
/**
|
|
868
|
+
* Generate updates with payload
|
|
869
|
+
* @remarks
|
|
870
|
+
* If `excludePayloadGeneration` is `true`, it emits the `bookmarkUpdates` directly.
|
|
871
|
+
* If `excludePayloadGeneration` is `false`, it generates the payload using the `generatePayload` method and emits the updated `bookmarkUpdates` with the generated payload.
|
|
872
|
+
*/
|
|
873
|
+
const updates$ = options?.excludePayloadGeneration
|
|
874
|
+
? of(bookmarkUpdates as BookmarkUpdate<T>)
|
|
875
|
+
: this.generatePayload<T>(bookmarkUpdates?.payload).pipe(
|
|
876
|
+
map(
|
|
877
|
+
(payload) =>
|
|
878
|
+
({
|
|
879
|
+
...bookmarkUpdates,
|
|
880
|
+
payload,
|
|
881
|
+
}) as BookmarkUpdate<T>,
|
|
882
|
+
),
|
|
883
|
+
);
|
|
844
884
|
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
885
|
+
// notify listeners that a bookmark is about to be updated
|
|
886
|
+
const dispatch$ = updates$.pipe(
|
|
887
|
+
switchMap(async (updates) => {
|
|
888
|
+
const { canceled, type } = await this._dispatchEvent('onBookmarkUpdate', {
|
|
889
|
+
detail: {
|
|
890
|
+
current: bookmarkSelector(this.#store.value, bookmarkId),
|
|
891
|
+
updates,
|
|
852
892
|
},
|
|
853
|
-
|
|
854
|
-
|
|
893
|
+
cancelable: true,
|
|
894
|
+
});
|
|
855
895
|
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
cause,
|
|
862
|
-
});
|
|
863
|
-
this._log?.info(error.message);
|
|
896
|
+
if (canceled) {
|
|
897
|
+
const error = new BookmarkProviderError(
|
|
898
|
+
`event: ${type} was canceled by listener for updating bookmark: ${bookmarkId}, ref: ${ref}`,
|
|
899
|
+
);
|
|
900
|
+
this._log?.warn(error.message, updates);
|
|
864
901
|
throw error;
|
|
865
|
-
}
|
|
866
|
-
timeout({
|
|
867
|
-
each: defaultTimeout,
|
|
868
|
-
with: () => {
|
|
869
|
-
throw new BookmarkProviderError(`Timeout while updating bookmark: ${bookmarkId}`);
|
|
870
|
-
},
|
|
871
|
-
}),
|
|
872
|
-
);
|
|
902
|
+
}
|
|
873
903
|
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
map(
|
|
878
|
-
// TODO: add payload if current bookmark is the same as the updated bookmark
|
|
879
|
-
({ payload }): Bookmark<T> =>
|
|
880
|
-
({
|
|
881
|
-
...bookmarkSelector(this.#store.value, payload.id),
|
|
882
|
-
payload: payload.payload,
|
|
883
|
-
}) as Bookmark<T>,
|
|
884
|
-
),
|
|
885
|
-
tap((bookmark) => {
|
|
886
|
-
this._log?.info(`Bookmark updated: ${bookmark.id}, ref: ${ref}`);
|
|
887
|
-
this._dispatchEvent('onBookmarkUpdated', { detail: bookmark });
|
|
888
|
-
}),
|
|
889
|
-
raceWith(failure$),
|
|
890
|
-
first(),
|
|
891
|
-
);
|
|
904
|
+
return updates;
|
|
905
|
+
}),
|
|
906
|
+
);
|
|
892
907
|
|
|
893
|
-
|
|
908
|
+
// Start the update process immediately
|
|
909
|
+
const updateSubscription = dispatch$.subscribe({
|
|
910
|
+
error: (error) => {
|
|
911
|
+
this._log?.error(`Failed to prepare bookmark update: ${bookmarkId}`, error);
|
|
912
|
+
},
|
|
913
|
+
next: (updates) => {
|
|
914
|
+
// trigger the store to update the bookmark
|
|
915
|
+
this.#store.next(bookmarkActions.updateBookmark({ bookmarkId, updates }, { ref }));
|
|
916
|
+
},
|
|
917
|
+
});
|
|
918
|
+
|
|
919
|
+
// monitor the failure case when updating a bookmark
|
|
920
|
+
const failure$ = action$.pipe(
|
|
921
|
+
filter(bookmarkActions.updateBookmark.failure.match),
|
|
922
|
+
map(({ payload: cause }) => {
|
|
923
|
+
const error = new BookmarkProviderError(`Failed to update bookmark: ${bookmarkId}`, {
|
|
924
|
+
cause,
|
|
925
|
+
});
|
|
926
|
+
this._log?.info(error.message);
|
|
927
|
+
throw error;
|
|
928
|
+
}),
|
|
929
|
+
timeout({
|
|
930
|
+
each: defaultTimeout,
|
|
931
|
+
with: () => {
|
|
932
|
+
throw new BookmarkProviderError(`Timeout while updating bookmark: ${bookmarkId}`);
|
|
933
|
+
},
|
|
934
|
+
}),
|
|
935
|
+
);
|
|
936
|
+
|
|
937
|
+
// monitor the success case when updating a bookmark
|
|
938
|
+
const request$ = action$.pipe(
|
|
939
|
+
filter(bookmarkActions.updateBookmark.success.match),
|
|
940
|
+
map(
|
|
941
|
+
// TODO: add payload if current bookmark is the same as the updated bookmark
|
|
942
|
+
({ payload }): Bookmark<T> =>
|
|
943
|
+
({
|
|
944
|
+
...bookmarkSelector(this.#store.value, payload.id),
|
|
945
|
+
payload: payload.payload,
|
|
946
|
+
}) as Bookmark<T>,
|
|
947
|
+
),
|
|
948
|
+
tap((bookmark) => {
|
|
949
|
+
this._log?.info(`Bookmark updated: ${bookmark.id}, ref: ${ref}`);
|
|
950
|
+
this._dispatchEvent('onBookmarkUpdated', { detail: bookmark });
|
|
951
|
+
}),
|
|
952
|
+
raceWith(failure$),
|
|
953
|
+
first(),
|
|
954
|
+
);
|
|
955
|
+
|
|
956
|
+
// Return the observable that will emit the result
|
|
957
|
+
return new Observable<Bookmark<T>>((subscriber) => {
|
|
958
|
+
// Clean up the update subscription when the result observable is unsubscribed
|
|
959
|
+
subscriber.add(() => updateSubscription.unsubscribe());
|
|
960
|
+
|
|
961
|
+
// Emit the updated bookmark
|
|
894
962
|
request$.subscribe(subscriber);
|
|
895
963
|
});
|
|
896
964
|
}
|
|
@@ -903,7 +971,7 @@ export class BookmarkProvider implements IBookmarkProvider {
|
|
|
903
971
|
* @param bookmark - The bookmark to update.
|
|
904
972
|
* @returns A promise that resolves to the updated bookmark with its associated data.
|
|
905
973
|
*/
|
|
906
|
-
public updateBookmarkAsync<T extends BookmarkData>(
|
|
974
|
+
public updateBookmarkAsync<T extends BookmarkData = BookmarkData>(
|
|
907
975
|
id_or_bookmark: string | Bookmark<T>,
|
|
908
976
|
updates_or_options?: BookmarkUpdate<T> | BookmarkUpdateOptions,
|
|
909
977
|
options?: BookmarkUpdateOptions,
|
|
@@ -921,10 +989,7 @@ export class BookmarkProvider implements IBookmarkProvider {
|
|
|
921
989
|
updatePayload: boolean;
|
|
922
990
|
}
|
|
923
991
|
).updatePayload,
|
|
924
|
-
})
|
|
925
|
-
// this is totally wrong, but we need to keep the API for now
|
|
926
|
-
map((bookmark) => bookmark.payload as Bookmark<T>),
|
|
927
|
-
),
|
|
992
|
+
}),
|
|
928
993
|
);
|
|
929
994
|
}
|
|
930
995
|
return lastValueFrom(
|
|
@@ -934,83 +999,88 @@ export class BookmarkProvider implements IBookmarkProvider {
|
|
|
934
999
|
|
|
935
1000
|
/**
|
|
936
1001
|
* Deletes a bookmark with the specified bookmarkId.
|
|
1002
|
+
* The deletion executes immediately when this method is called.
|
|
937
1003
|
*
|
|
938
1004
|
* @param bookmarkId - The unique identifier of the bookmark to be deleted.
|
|
939
|
-
* @returns
|
|
1005
|
+
* @returns An Observable that emits when the bookmark is successfully deleted.
|
|
940
1006
|
* @throws {BookmarkProviderError} If there is an error deleting the bookmark.
|
|
941
1007
|
*/
|
|
942
1008
|
public deleteBookmark(bookmarkId: string): Observable<void> {
|
|
943
|
-
|
|
944
|
-
const { ref, action$ } = this._useScopedActions();
|
|
945
|
-
|
|
946
|
-
this._log?.debug(`Removing bookmark: ${bookmarkId}, ref: ${ref}`);
|
|
1009
|
+
const { ref, action$ } = this._useScopedActions();
|
|
947
1010
|
|
|
948
|
-
|
|
949
|
-
const bookmark = bookmarkSelector(this.#store.value, bookmarkId) ?? {
|
|
950
|
-
id: bookmarkId,
|
|
951
|
-
};
|
|
952
|
-
|
|
953
|
-
// observable that dispatches the 'onBookmarkDelete' event and maps the result
|
|
954
|
-
const dispatch$ = from(
|
|
955
|
-
this._dispatchEvent('onBookmarkDelete', {
|
|
956
|
-
detail: bookmark,
|
|
957
|
-
cancelable: true,
|
|
958
|
-
}),
|
|
959
|
-
).pipe(
|
|
960
|
-
map(({ canceled, type, detail }) => {
|
|
961
|
-
if (canceled) {
|
|
962
|
-
const error = new BookmarkProviderError(
|
|
963
|
-
`event: ${type} was canceled by listener for removing bookmark: ${bookmarkId}, ref: ${ref}`,
|
|
964
|
-
);
|
|
965
|
-
this._log?.warn(error.message);
|
|
966
|
-
throw error;
|
|
967
|
-
}
|
|
968
|
-
return detail;
|
|
969
|
-
}),
|
|
970
|
-
);
|
|
1011
|
+
this._log?.debug(`Removing bookmark: ${bookmarkId}, ref: ${ref}`);
|
|
971
1012
|
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
next: (bookmark) => {
|
|
977
|
-
// request the store to delete the bookmark
|
|
978
|
-
this.#store.next(bookmarkActions.deleteBookmark(bookmark.id, { ref }));
|
|
979
|
-
},
|
|
980
|
-
}),
|
|
981
|
-
);
|
|
1013
|
+
// bookmark to delete
|
|
1014
|
+
const bookmark = bookmarkSelector(this.#store.value, bookmarkId) ?? {
|
|
1015
|
+
id: bookmarkId,
|
|
1016
|
+
};
|
|
982
1017
|
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
1018
|
+
// observable that dispatches the 'onBookmarkDelete' event and maps the result
|
|
1019
|
+
const dispatch$ = from(
|
|
1020
|
+
this._dispatchEvent('onBookmarkDelete', {
|
|
1021
|
+
detail: bookmark,
|
|
1022
|
+
cancelable: true,
|
|
1023
|
+
}),
|
|
1024
|
+
).pipe(
|
|
1025
|
+
map(({ canceled, type, detail }) => {
|
|
1026
|
+
if (canceled) {
|
|
1027
|
+
const error = new BookmarkProviderError(
|
|
1028
|
+
`event: ${type} was canceled by listener for removing bookmark: ${bookmarkId}, ref: ${ref}`,
|
|
1029
|
+
);
|
|
990
1030
|
this._log?.warn(error.message);
|
|
991
1031
|
throw error;
|
|
992
|
-
}
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
throw new BookmarkProviderError(`Timeout while updating bookmark: ${bookmarkId}`);
|
|
997
|
-
},
|
|
998
|
-
}),
|
|
999
|
-
);
|
|
1032
|
+
}
|
|
1033
|
+
return detail;
|
|
1034
|
+
}),
|
|
1035
|
+
);
|
|
1000
1036
|
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
})
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
);
|
|
1037
|
+
// Start the deletion process immediately
|
|
1038
|
+
const deleteSubscription = dispatch$.subscribe({
|
|
1039
|
+
error: (error) => {
|
|
1040
|
+
this._log?.error(`Failed to prepare bookmark deletion: ${bookmarkId}`, error);
|
|
1041
|
+
},
|
|
1042
|
+
next: (bookmark) => {
|
|
1043
|
+
// request the store to delete the bookmark
|
|
1044
|
+
this.#store.next(bookmarkActions.deleteBookmark(bookmark.id, { ref }));
|
|
1045
|
+
},
|
|
1046
|
+
});
|
|
1012
1047
|
|
|
1013
|
-
|
|
1048
|
+
// monitor the failure case when deleting a bookmark
|
|
1049
|
+
const failure$ = action$.pipe(
|
|
1050
|
+
filter(bookmarkActions.deleteBookmark.failure.match),
|
|
1051
|
+
map(({ payload: cause }) => {
|
|
1052
|
+
const error = new BookmarkProviderError(`Failed to delete bookmark: ${bookmarkId}`, {
|
|
1053
|
+
cause,
|
|
1054
|
+
});
|
|
1055
|
+
this._log?.warn(error.message);
|
|
1056
|
+
throw error;
|
|
1057
|
+
}),
|
|
1058
|
+
timeout({
|
|
1059
|
+
each: defaultTimeout,
|
|
1060
|
+
with: () => {
|
|
1061
|
+
throw new BookmarkProviderError(`Timeout while deleting bookmark: ${bookmarkId}`);
|
|
1062
|
+
},
|
|
1063
|
+
}),
|
|
1064
|
+
);
|
|
1065
|
+
|
|
1066
|
+
// monitor the success case when deleting a bookmark
|
|
1067
|
+
const request$ = action$.pipe(
|
|
1068
|
+
filter(bookmarkActions.deleteBookmark.success.match),
|
|
1069
|
+
map(() => undefined),
|
|
1070
|
+
tap(() => {
|
|
1071
|
+
this._log?.info(`Removed bookmark: ${bookmark.id}, ref: ${ref}`);
|
|
1072
|
+
this._dispatchEvent('onBookmarkDeleted', { detail: bookmark });
|
|
1073
|
+
}),
|
|
1074
|
+
raceWith(failure$),
|
|
1075
|
+
first(),
|
|
1076
|
+
);
|
|
1077
|
+
|
|
1078
|
+
// Return the observable that will emit the result
|
|
1079
|
+
return new Observable<void>((subscriber) => {
|
|
1080
|
+
// Clean up the delete subscription when the result observable is unsubscribed
|
|
1081
|
+
subscriber.add(() => deleteSubscription.unsubscribe());
|
|
1082
|
+
|
|
1083
|
+
// Emit the deletion result
|
|
1014
1084
|
request$.subscribe(subscriber);
|
|
1015
1085
|
});
|
|
1016
1086
|
}
|
|
@@ -1198,7 +1268,7 @@ export class BookmarkProvider implements IBookmarkProvider {
|
|
|
1198
1268
|
this._log?.info(`Removed bookmark: ${bookmarkId} from favourites, ref: ${ref}`);
|
|
1199
1269
|
this._dispatchEvent('onBookmarkFavouriteRemoved', { detail: bookmark });
|
|
1200
1270
|
}),
|
|
1201
|
-
map(() =>
|
|
1271
|
+
map(() => undefined),
|
|
1202
1272
|
raceWith(failure$),
|
|
1203
1273
|
first(),
|
|
1204
1274
|
);
|
|
@@ -1290,7 +1360,7 @@ export class BookmarkProvider implements IBookmarkProvider {
|
|
|
1290
1360
|
return success$.pipe(raceWith(failure$));
|
|
1291
1361
|
}
|
|
1292
1362
|
|
|
1293
|
-
protected _getBookmarkData<T extends BookmarkData>(
|
|
1363
|
+
protected _getBookmarkData<T extends BookmarkData = BookmarkData>(
|
|
1294
1364
|
bookmarkId: string,
|
|
1295
1365
|
options?: { timeout?: number },
|
|
1296
1366
|
): Observable<T | null> {
|
|
@@ -1364,16 +1434,16 @@ export class BookmarkProvider implements IBookmarkProvider {
|
|
|
1364
1434
|
/**
|
|
1365
1435
|
* Generates a unique identifier for the operation
|
|
1366
1436
|
*/
|
|
1367
|
-
ref
|
|
1437
|
+
const operationRef = ref ?? generateGUID();
|
|
1368
1438
|
|
|
1369
1439
|
/**
|
|
1370
1440
|
* Observable stream of actions filtered by a specific reference.
|
|
1371
1441
|
*/
|
|
1372
1442
|
const action$ = this.#store.action$.pipe(
|
|
1373
|
-
filter((action): action is TAction => 'meta' in action && action.meta?.ref ===
|
|
1443
|
+
filter((action): action is TAction => 'meta' in action && action.meta?.ref === operationRef),
|
|
1374
1444
|
);
|
|
1375
1445
|
|
|
1376
|
-
return { ref, action$ };
|
|
1446
|
+
return { ref: operationRef, action$ };
|
|
1377
1447
|
}
|
|
1378
1448
|
|
|
1379
1449
|
/**
|