@equinor/fusion-framework-module-bookmark 4.1.0 → 4.1.1

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 (42) hide show
  1. package/dist/esm/version.js +1 -1
  2. package/dist/tsconfig.tsbuildinfo +1 -1
  3. package/dist/types/version.d.ts +1 -1
  4. package/package.json +13 -10
  5. package/CHANGELOG.md +0 -821
  6. package/src/BookmarkClient.interface.ts +0 -157
  7. package/src/BookmarkClient.ts +0 -290
  8. package/src/BookmarkFlowError.ts +0 -38
  9. package/src/BookmarkModuleConfigurator.ts +0 -412
  10. package/src/BookmarkProvider.events.ts +0 -87
  11. package/src/BookmarkProvider.interface.ts +0 -180
  12. package/src/BookmarkProvider.selectors.ts +0 -69
  13. package/src/BookmarkProvider.ts +0 -1549
  14. package/src/BookmarkProviderError.ts +0 -19
  15. package/src/__tests__/mock/bookmark-mock.test.ts +0 -199
  16. package/src/bookmark-actions.ts +0 -132
  17. package/src/bookmark-config.schema.ts +0 -55
  18. package/src/bookmark-flows/bookmark-api-flows.ts +0 -45
  19. package/src/bookmark-flows/handle-add-bookmark-as-favorite.ts +0 -49
  20. package/src/bookmark-flows/handle-create-bookmark.ts +0 -47
  21. package/src/bookmark-flows/handle-delete-bookmark.ts +0 -47
  22. package/src/bookmark-flows/handle-fetch-all-bookmark.ts +0 -53
  23. package/src/bookmark-flows/handle-fetch-bookmark-data.ts +0 -58
  24. package/src/bookmark-flows/handle-fetch-bookmark.ts +0 -64
  25. package/src/bookmark-flows/handle-remove-bookmark-from-favorites.ts +0 -49
  26. package/src/bookmark-flows/handle-remove-bookmark.ts +0 -76
  27. package/src/bookmark-flows/handle-update-bookmark.ts +0 -48
  28. package/src/bookmark-flows/index.ts +0 -10
  29. package/src/bookmark-module.ts +0 -98
  30. package/src/bookmark.schemas.ts +0 -81
  31. package/src/create-bookmark-reducer.ts +0 -149
  32. package/src/create-bookmark-store.ts +0 -61
  33. package/src/enable-bookmark.ts +0 -44
  34. package/src/index.ts +0 -43
  35. package/src/mock/BookmarkMockClient.ts +0 -310
  36. package/src/mock/BookmarkMockConfigurator.ts +0 -159
  37. package/src/mock/index.ts +0 -25
  38. package/src/mock/module.ts +0 -64
  39. package/src/types.ts +0 -121
  40. package/src/version.ts +0 -2
  41. package/tsconfig.json +0 -30
  42. package/vitest.config.ts +0 -11
@@ -1,58 +0,0 @@
1
- import { of, from } from 'rxjs';
2
- import {
3
- throttleTime,
4
- groupBy,
5
- mergeMap,
6
- switchMap,
7
- map,
8
- catchError,
9
- filter,
10
- last,
11
- } from 'rxjs/operators';
12
-
13
- import type { Flow } from '@equinor/fusion-observable';
14
-
15
- import { bookmarkActions as actions, type BookmarkActions } from '../bookmark-actions';
16
- import type { IBookmarkClient } from '../BookmarkClient.interface';
17
- import { BookmarkFlowError } from '../BookmarkFlowError';
18
-
19
- const defaultThrottleTime = 200;
20
-
21
- /**
22
- * Creates a flow for handling fetching bookmark data.
23
- *
24
- * @param api - The bookmark client API.
25
- * @returns A flow of bookmark actions.
26
- */
27
- export const handleFetchBookmarkData =
28
- (api: IBookmarkClient): Flow<BookmarkActions> =>
29
- (action$) => {
30
- // throttle requests per bookmark id, then fetch the data and dispatch success/failure
31
- const flow$ = action$.pipe(
32
- filter(actions.fetchBookmarkData.match),
33
- // group requests by bookmark id so throttling only applies per-id
34
- groupBy((action) => JSON.stringify(action.payload)),
35
- mergeMap((group) =>
36
- // avoid flooding the API with repeated requests for the same bookmark
37
- group.pipe(throttleTime(defaultThrottleTime)),
38
- ),
39
- switchMap((action) => {
40
- // wait for the final emission before mapping to a success/failure action
41
- return from(api.getBookmarkData(action.payload)).pipe(
42
- last(),
43
- map((data) => actions.fetchBookmarkData.success(action.payload, data, action.meta)),
44
- catchError((error) =>
45
- of(
46
- actions.fetchBookmarkData.failure(
47
- new BookmarkFlowError('Failed to fetch bookmark payload', action, {
48
- cause: error,
49
- }),
50
- action.meta,
51
- ),
52
- ),
53
- ),
54
- );
55
- }),
56
- );
57
- return flow$;
58
- };
@@ -1,64 +0,0 @@
1
- import { of } from 'rxjs';
2
- import {
3
- throttleTime,
4
- groupBy,
5
- mergeMap,
6
- switchMap,
7
- map,
8
- catchError,
9
- filter,
10
- last,
11
- } from 'rxjs/operators';
12
-
13
- import { from } from 'rxjs';
14
-
15
- import type { Flow } from '@equinor/fusion-observable';
16
-
17
- import { bookmarkActions as actions, type BookmarkActions } from '../bookmark-actions';
18
- import type { IBookmarkClient } from '../BookmarkClient.interface';
19
- import { BookmarkFlowError } from '../BookmarkFlowError';
20
-
21
- const defaultThrottleTime = 200;
22
-
23
- /**
24
- * Handles the fetch bookmark action by making an API request to get the bookmark by its ID.
25
- *
26
- * @param api - The bookmark client API.
27
- * @returns A flow that handles the fetch bookmark action.
28
- */
29
- export const handleFetchBookmark =
30
- (api: IBookmarkClient): Flow<BookmarkActions> =>
31
- (action$) => {
32
- /**
33
- * Observable that represents the flow of fetching bookmarks.
34
- * It listens for `fetchBookmark` actions, makes an API request to get the bookmark by ID,
35
- * and emits corresponding success or failure actions based on the API response.
36
- */
37
- const flow$ = action$.pipe(
38
- filter(actions.fetchBookmark.match),
39
- // group requests by bookmark id so throttling only applies per-id
40
- groupBy((action) => action.payload),
41
- mergeMap((group) =>
42
- // avoid flooding the API with repeated requests for the same bookmark
43
- group.pipe(throttleTime(defaultThrottleTime)),
44
- ),
45
- switchMap((action) =>
46
- // Map the API call outcome to a success or failure action for this bookmark.
47
- from(api.getBookmarkById(action.payload)).pipe(
48
- last(),
49
- map((bookmark) => actions.fetchBookmark.success(bookmark, action.meta)),
50
- catchError((error) =>
51
- of(
52
- actions.fetchBookmark.failure(
53
- new BookmarkFlowError('Failed to fetch bookmark', action, {
54
- cause: error,
55
- }),
56
- action.meta,
57
- ),
58
- ),
59
- ),
60
- ),
61
- ),
62
- );
63
- return flow$;
64
- };
@@ -1,49 +0,0 @@
1
- import { of, from } from 'rxjs';
2
- import { concatMap, map, catchError, filter, last } from 'rxjs/operators';
3
-
4
- import type { Flow, Observable } from '@equinor/fusion-observable';
5
-
6
- import { bookmarkActions as actions, type BookmarkActions } from '../bookmark-actions';
7
- import type { IBookmarkClient } from '../BookmarkClient.interface';
8
- import { BookmarkFlowError } from '../BookmarkFlowError';
9
-
10
- /**
11
- * Creates a Flow for handling removing a bookmark from favorites.
12
- *
13
- * @param api - An instance of the `IBookmarkClient` interface, which provides the necessary API methods for managing favorite bookmarks.
14
- * @returns A flow that listens for `removeBookmarkAsFavorite` actions, removes the bookmark from the user's favorites using the provided API.
15
- */
16
- export const handleRemoveBookmarkFromFavorites =
17
- (api: IBookmarkClient): Flow<BookmarkActions> =>
18
- (action$: Observable<BookmarkActions>) => {
19
- /**
20
- * - Listens for the `removeBookmarkAsFavourite` action.
21
- * - Calls the `api.removeBookmarkFromFavorites` function to remove the bookmark from favorites.
22
- * - On success, dispatches the `removeBookmarkAsFavourite.success` action.
23
- * - On error, dispatches the `fetchBookmark.failure` action with a `BookmarkFlowError`.
24
- * - Uses `concatMap` to prevent aborting the request if a new action is dispatched while the previous request is in flight.
25
- */
26
- const flow$ = action$.pipe(
27
- filter(actions.removeBookmarkAsFavourite.match),
28
- // use concatMap to prevent aborting the request if a new action is dispatched while the previous request is in flight
29
- concatMap((action) =>
30
- // Map the API call outcome to a success or failure action for this bookmark.
31
- from(api.removeBookmarkFromFavorites(action.payload)).pipe(
32
- last(),
33
- map(() => actions.removeBookmarkAsFavourite.success(action.payload, action.meta)),
34
- catchError((error) =>
35
- of(
36
- actions.removeBookmarkAsFavourite.failure(
37
- new BookmarkFlowError('Failed to remove bookmark as favorite', action, {
38
- cause: error,
39
- }),
40
- action.meta,
41
- ),
42
- ),
43
- ),
44
- ),
45
- ),
46
- );
47
-
48
- return flow$;
49
- };
@@ -1,76 +0,0 @@
1
- import { concat, merge, from } from 'rxjs';
2
- import { map, concatMap, filter } from 'rxjs/operators';
3
-
4
- import { type Flow, getBaseType } from '@equinor/fusion-observable';
5
-
6
- import { bookmarkActions as actions, type BookmarkActions } from '../bookmark-actions';
7
- import type { IBookmarkClient } from '../BookmarkClient.interface';
8
-
9
- /**
10
- * Creates a flow for handling bookmark removal.
11
- *
12
- * This function observes actions related to deleting a bookmark or removing a bookmark as a favorite, and dispatches the appropriate actions for each case.
13
- * If the bookmark is currently a favorite, it will dispatch an action to remove it as a favorite.
14
- * If the bookmark is not a favorite, it will dispatch an action to delete it.
15
- *
16
- * @param api - An instance of the `IBookmarkClient` interface, which provides the necessary API methods checking if bookmarks are favorites.
17
- * @returns A flow that handles the removal of a bookmark.
18
- */
19
- export const handleRemoveBookmark =
20
- (api: IBookmarkClient): Flow<BookmarkActions> =>
21
- (action$) => {
22
- /**
23
- * Handles the removal of a bookmark from the application.
24
- *
25
- * This function is responsible for determining whether the bookmark being removed is a favorite or not,
26
- * and then dispatching the appropriate action to either remove the bookmark as a favorite or delete the bookmark entirely.
27
- *
28
- * Uses `concatMap` to prevent aborting the request if a new action is dispatched while the previous request is in flight.
29
- */
30
- const dispatch$ = action$.pipe(
31
- filter(actions.removeBookmark.match),
32
- concatMap(({ payload: bookmarkId }) => {
33
- // pick which action to dispatch based on the bookmark's favorite status
34
- return from(api.isBookmarkFavorite(bookmarkId)).pipe(
35
- map((isFavorite) => {
36
- const action = isFavorite ? actions.removeBookmarkAsFavourite : actions.deleteBookmark;
37
- return action(bookmarkId);
38
- }),
39
- );
40
- }),
41
- );
42
-
43
- /**
44
- * Handles failures for deleting a bookmark or removing a bookmark as a favorite.
45
- */
46
- const failure$ = merge(
47
- action$.pipe(filter(actions.deleteBookmark.failure.match)),
48
- action$.pipe(filter(actions.removeBookmarkAsFavourite.failure.match)),
49
- ).pipe(map((action) => actions.removeBookmark.failure(action.payload, action.meta)));
50
-
51
- /**
52
- * Handles the success of deleting a bookmark or removing a bookmark as a favorite.
53
- */
54
- const success$ = merge(
55
- action$.pipe(filter(actions.deleteBookmark.success.match)),
56
- action$.pipe(filter(actions.removeBookmarkAsFavourite.success.match)),
57
- ).pipe(
58
- map((action) =>
59
- actions.removeBookmark.success(
60
- {
61
- type: getBaseType(action.type),
62
- bookmarkId: action.payload,
63
- },
64
- action.meta,
65
- ),
66
- ),
67
- );
68
-
69
- /**
70
- * First dispatch either deletions of bookmark or removals of bookmarks as favorites.
71
- * Then observer the results of those actions and dispatch the appropriate actions.
72
- */
73
- const flow$ = concat(dispatch$, merge(failure$, success$));
74
-
75
- return flow$;
76
- };
@@ -1,48 +0,0 @@
1
- import { of, from } from 'rxjs';
2
- import { concatMap, map, catchError, filter, last } from 'rxjs/operators';
3
-
4
- import type { Flow, Observable } from '@equinor/fusion-observable';
5
-
6
- import { bookmarkActions as actions, type BookmarkActions } from '../bookmark-actions';
7
- import type { IBookmarkClient } from '../BookmarkClient.interface';
8
- import { BookmarkFlowError } from '../BookmarkFlowError';
9
-
10
- /**
11
- * Creates a Flow for handling updating bookmarks.
12
- *
13
- * @param api - An instance of the `IBookmarkClient` interface, which provides the necessary API methods for updating bookmarks.
14
- * @returns A flow that listens for `updateBookmark` actions, updates the bookmark using the provided API.
15
- */
16
- export const handleUpdateBookmark =
17
- (api: IBookmarkClient): Flow<BookmarkActions> =>
18
- (action$: Observable<BookmarkActions>) => {
19
- /**
20
- * This flow listens for `updateBookmark` actions, then calls the `api.updateBookmark` function with the action payload.
21
- * If the update is successful, it dispatches a `updateBookmark.success` action with the updated bookmark.
22
- * If there is an error, it dispatches a `fetchBookmark.failure` action with the error.
23
- *
24
- * The `concatMap` operator is used to prevent aborting the request if a new `updateBookmark` action is dispatched while the previous request is in flight.
25
- */
26
- const flow$ = action$.pipe(
27
- filter(actions.updateBookmark.match),
28
- concatMap((action) => {
29
- const { bookmarkId, updates } = action.payload;
30
- // wait for the final emission before mapping to a success/failure action
31
- return from(api.updateBookmark(bookmarkId, updates)).pipe(
32
- last(),
33
- map((bookmark) => actions.updateBookmark.success(bookmark, action.meta)),
34
- catchError((error) =>
35
- of(
36
- actions.updateBookmark.failure(
37
- new BookmarkFlowError('Failed to update bookmark', action, {
38
- cause: error,
39
- }),
40
- action.meta,
41
- ),
42
- ),
43
- ),
44
- );
45
- }),
46
- );
47
- return flow$;
48
- };
@@ -1,10 +0,0 @@
1
- export { handleFetchBookmark } from './handle-fetch-bookmark';
2
- export { handleFetchBookmarkData } from './handle-fetch-bookmark-data';
3
- export { handleFetchAllBookmark } from './handle-fetch-all-bookmark';
4
- export { handleCreateBookmark } from './handle-create-bookmark';
5
- export { handleUpdateBookmark } from './handle-update-bookmark';
6
- export { handleDeleteBookmark } from './handle-delete-bookmark';
7
- export { handleRemoveBookmark } from './handle-remove-bookmark';
8
- export { handleRemoveBookmarkFromFavorites } from './handle-remove-bookmark-from-favorites';
9
- export { handleAddBookmarkAsFavorite } from './handle-add-bookmark-as-favorite';
10
- export { bookmarkApiFlows } from './bookmark-api-flows';
@@ -1,98 +0,0 @@
1
- import {
2
- type Module,
3
- type ModulesInstance,
4
- SemanticVersion,
5
- } from '@equinor/fusion-framework-module';
6
- import type { EventModule } from '@equinor/fusion-framework-module-event';
7
- import type { ServicesModule } from '@equinor/fusion-framework-module-services';
8
- import type { AppModule } from '@equinor/fusion-framework-module-app';
9
- import type { ContextModule } from '@equinor/fusion-framework-module-context';
10
- import { BookmarkProvider } from './BookmarkProvider';
11
- import { BookmarkModuleConfigurator } from './BookmarkModuleConfigurator';
12
- import { ConsoleLogger, type ILogger } from '@equinor/fusion-log';
13
- import { lastValueFrom } from 'rxjs';
14
- import { version } from './version';
15
- import type { IBookmarkProvider } from './BookmarkProvider.interface';
16
-
17
- /** String literal key used to register the bookmark module in the framework. */
18
- export type BookmarkModuleKey = 'bookmark';
19
-
20
- /** The module key constant used to identify the bookmark module at runtime. */
21
- export const moduleKey: BookmarkModuleKey = 'bookmark';
22
-
23
- /**
24
- * Type definition for the bookmark framework module.
25
- *
26
- * Declares the module key, provider interface, configurator class,
27
- * and optional peer dependencies (event, services, app, context modules).
28
- */
29
- export type BookmarkModule = Module<
30
- BookmarkModuleKey,
31
- IBookmarkProvider,
32
- BookmarkModuleConfigurator,
33
- [EventModule, ServicesModule, AppModule, ContextModule]
34
- >;
35
-
36
- // TODO(#5134) - remove when all framework uses log
37
- const fallbackLogger: ILogger = new ConsoleLogger('BookmarkModule');
38
-
39
- /**
40
- * Bookmark module definition for the Fusion Framework.
41
- *
42
- * Handles configuration, initialization (creating a {@link BookmarkProvider}),
43
- * and disposal of the bookmark module lifecycle.
44
- *
45
- * @example
46
- * ```ts
47
- * import { enableBookmark } from '@equinor/fusion-framework-module-bookmark';
48
- *
49
- * const configure = (configurator) => {
50
- * enableBookmark(configurator);
51
- * };
52
- * ```
53
- */
54
- export const module: BookmarkModule = {
55
- name: moduleKey,
56
- version: new SemanticVersion(version),
57
- configure: (args) => {
58
- // use parent logger if available, else fallback to console logger
59
- const log: ILogger =
60
- (args?.log as ILogger)?.createSubLogger('BookmarkModule') || fallbackLogger;
61
-
62
- // Set client from parent module if available
63
- const ref = args?.ref as ModulesInstance<[BookmarkModule]>;
64
-
65
- // create a configurator instance
66
- const configurator = new BookmarkModuleConfigurator({ log, ref });
67
-
68
- return configurator;
69
- },
70
- initialize: async (args) => {
71
- const parent = args.ref?.bookmark as BookmarkProvider;
72
- const config = await lastValueFrom(
73
- args.config.createConfig(args, {
74
- filters: parent?.filters,
75
- sourceSystem: parent?.sourceSystem,
76
- resolve: parent
77
- ? {
78
- application: parent.resolvedApplication.bind(parent),
79
- context: parent.resolvedContext.bind(parent),
80
- }
81
- : undefined,
82
- }),
83
- );
84
- const provider = new BookmarkProvider(config);
85
- return provider;
86
- },
87
- dispose: (args) => {
88
- (args.instance as BookmarkProvider).dispose();
89
- },
90
- };
91
-
92
- declare module '@equinor/fusion-framework-module' {
93
- interface Modules {
94
- bookmark: BookmarkModule;
95
- }
96
- }
97
-
98
- export default module;
@@ -1,81 +0,0 @@
1
- import * as z from 'zod';
2
- import type { Bookmark, BookmarkData } from './types';
3
-
4
- /** Zod schema for validating {@link BookmarkUser} objects. */
5
- export const bookmarkUserSchema = z.object({
6
- id: z.string(),
7
- name: z.string(),
8
- mail: z.string().optional(),
9
- });
10
-
11
- /** Zod schema for validating {@link SourceSystem} objects on a bookmark. */
12
- export const bookmarkSourceSystemSchema = z.object(
13
- {
14
- identifier: z.string(),
15
- name: z.string().nullish(),
16
- subSystem: z.string().nullish(),
17
- },
18
- { message: 'invalid source system' },
19
- );
20
-
21
- /** Zod schema for validating {@link BookmarkContext} objects. */
22
- export const bookmarkContextSchema = z.object({
23
- id: z.string(),
24
- name: z.string().optional(),
25
- type: z.string().optional(),
26
- });
27
-
28
- /** Zod schema for validating a {@link BookmarkWithoutData} (no payload). */
29
- export const bookmarkSchema = z.object({
30
- id: z.string(),
31
- name: z.string(),
32
- appKey: z.string(),
33
- description: z.string().optional(),
34
- isShared: z.boolean().optional(),
35
- created: z.date(),
36
- createdBy: bookmarkUserSchema,
37
- updated: z.date().optional(),
38
- updatedBy: bookmarkUserSchema.optional(),
39
- context: bookmarkContextSchema.optional(),
40
- sourceSystem: bookmarkSourceSystemSchema.nullish(),
41
- });
42
-
43
- /** Zod schema for validating an array of bookmarks. */
44
- export const bookmarksSchema = z.array(bookmarkSchema);
45
-
46
- /**
47
- * Creates a Zod schema for a {@link Bookmark} that includes a typed payload field.
48
- *
49
- * @template T - The bookmark payload data shape.
50
- * @template S - Zod schema type for the payload.
51
- * @param schema - Optional Zod schema to validate the payload. Defaults to
52
- * `z.record(z.string(), z.unknown()).or(z.string()).optional()`.
53
- * @returns A Zod object schema extending {@link bookmarkSchema} with a `payload` property.
54
- */
55
- export const bookmarkWithDataSchema = <
56
- T extends BookmarkData = BookmarkData,
57
- S extends z.ZodSchema<T> = z.ZodSchema<T>,
58
- >(
59
- schema?: S,
60
- ) => {
61
- // No payload schema was supplied — fall back to accepting any JSON-serializable value.
62
- // The generic constraint `S extends z.ZodSchema<T>` can't express this default directly,
63
- // so the fallback schema must be cast through `unknown` to satisfy `S`.
64
- const payloadSchema =
65
- schema ?? (z.record(z.string(), z.unknown()).or(z.string()).optional() as unknown as S);
66
- return bookmarkSchema.extend({
67
- payload: payloadSchema,
68
- });
69
- };
70
-
71
- /**
72
- * Parses an unknown value into a typed {@link Bookmark} using {@link bookmarkWithDataSchema}.
73
- *
74
- * @template T - The bookmark payload data shape.
75
- * @param value - The raw value to parse.
76
- * @returns The parsed bookmark object.
77
- * @throws {ZodError} When validation fails.
78
- */
79
- export const parseBookmark = <T extends BookmarkData>(value: unknown): Bookmark<T> => {
80
- return bookmarkWithDataSchema().parse(value) as Bookmark<T>;
81
- };
@@ -1,149 +0,0 @@
1
- import {
2
- createReducer,
3
- getBaseType,
4
- isCompleteAction,
5
- isFailureAction,
6
- isRequestAction,
7
- isSuccessAction,
8
- type ActionBaseType,
9
- } from '@equinor/fusion-observable';
10
-
11
- import { bookmarkActions, type BookmarkActions } from './bookmark-actions';
12
- import type { BookmarkState } from './create-bookmark-store';
13
- import type { BookmarkFlowError } from './BookmarkFlowError';
14
- import type { BookmarkWithoutData } from './types';
15
- import { enableMapSet } from 'immer';
16
-
17
- enableMapSet();
18
-
19
- /**
20
- * Utility function that extracts the base action type from a given action object.
21
- */
22
- const getBookmarkBaseAction = <T extends BookmarkActions>(action: T): ActionBaseType<T> => {
23
- return getBaseType(action.type) as ActionBaseType<T>;
24
- };
25
-
26
- /**
27
- * The default initial state for the BookmarkProvider reducer.
28
- */
29
- const defaultInitialState: BookmarkState = {
30
- status: new Set<ActionBaseType<BookmarkActions>>(),
31
- errors: {} as Record<ActionBaseType<BookmarkActions>, BookmarkFlowError>,
32
- bookmarks: {},
33
- };
34
-
35
- /**
36
- * Creates a reducer for managing the state of bookmarks.
37
- *
38
- * @todo TODO(#5135) - add fast-deep-equal to compare bookmarks
39
- *
40
- * @param initialState - The initial state of the bookmarks.
41
- * @returns A reducer function for managing the bookmarks state.
42
- */
43
- export const createBookmarkReducer = (initialState?: Partial<BookmarkState>) => {
44
- // Layer any provided overrides on top of the module's defaults for the initial state.
45
- const initial = { ...defaultInitialState, ...initialState };
46
- return createReducer<BookmarkState, BookmarkActions>(initial, (builder) => {
47
- builder
48
- .addCase(bookmarkActions.fetchBookmark.success, (state, action) => {
49
- // only update the bookmark if it already exists in the store
50
- if (action.payload.id in state.bookmarks) {
51
- state.bookmarks[action.payload.id] = action.payload;
52
- }
53
- })
54
- .addCase(bookmarkActions.fetchBookmarkData.success, (state, action) => {
55
- const { bookmarkId, data } = action.payload;
56
- // only apply the fetched data if it belongs to the current bookmark
57
- if (state.currentBookmark?.id === bookmarkId) {
58
- state.currentBookmark.payload = data;
59
- }
60
- })
61
- .addCase(bookmarkActions.fetchBookmarks.success, (state, action) => {
62
- // normalize the bookmarks array into a record
63
- // build a lookup record keyed by bookmark id
64
- state.bookmarks = action.payload.reduce(
65
- (acc, bookmark) => {
66
- acc[bookmark.id] = bookmark;
67
- return acc;
68
- },
69
- {} as Record<string, BookmarkWithoutData>,
70
- );
71
- })
72
- .addCase(bookmarkActions.setBookmark, (state, action) => {
73
- const bookmarkId = action.payload.id;
74
- // only update the bookmark if it already exists in the store
75
- if (bookmarkId in state.bookmarks) {
76
- state.bookmarks[bookmarkId] = action.payload;
77
- }
78
- // keep the current bookmark in sync if it's the one being set
79
- if (state.currentBookmark?.id === bookmarkId) {
80
- state.currentBookmark = action.payload;
81
- }
82
- })
83
- .addCase(bookmarkActions.setCurrentBookmark, (state, action) => {
84
- state.currentBookmark = action.payload;
85
- })
86
- .addCase(bookmarkActions.createBookmark.success, (state, action) => {
87
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
88
- const { payload, ...bookmark } = action.payload;
89
- state.bookmarks[bookmark.id] = bookmark;
90
- })
91
- .addCase(bookmarkActions.updateBookmark.success, (state, action) => {
92
- const bookmarkId = action.payload.id;
93
- const hasBookmark = bookmarkId in state.bookmarks;
94
- const isCurrent = state.currentBookmark?.id === bookmarkId;
95
-
96
- // get the current bookmark
97
- const current = hasBookmark
98
- ? state.bookmarks[bookmarkId]
99
- : isCurrent
100
- ? state.currentBookmark
101
- : null;
102
-
103
- // merge the current bookmark with the new data
104
- const next = { ...current, ...action.payload };
105
-
106
- // if the bookmark is in the current state, update it
107
- if (hasBookmark) {
108
- state.bookmarks[bookmarkId] = next;
109
- }
110
-
111
- // if the bookmark is the selected bookmark, update it
112
- if (isCurrent) {
113
- state.currentBookmark = next;
114
- }
115
- })
116
-
117
- /** removal of bookmarks */
118
- .addCase(bookmarkActions.deleteBookmark.success, (state, action) => {
119
- delete state.bookmarks[action.payload];
120
- })
121
- .addCase(bookmarkActions.removeBookmarkAsFavourite.success, (state, action) => {
122
- delete state.bookmarks[action.payload];
123
- })
124
-
125
- /** when a request is made, add the action type to the status object */
126
- .addMatcher(isRequestAction, (state, action) => {
127
- const actionName = getBookmarkBaseAction(action);
128
- state.status.add(actionName);
129
- })
130
-
131
- /** when a request succeeds, remove the error from the errors object */
132
- .addMatcher(isSuccessAction, (state, action) => {
133
- const actionName = getBookmarkBaseAction(action);
134
- delete state.errors[actionName];
135
- })
136
-
137
- /** when a request fails, add the error to the errors object */
138
- .addMatcher(isFailureAction, (state, action) => {
139
- const actionName = getBookmarkBaseAction(action);
140
- state.errors[actionName] = action.payload;
141
- })
142
-
143
- /** when a request is complete, remove the status from the status set */
144
- .addMatcher(isCompleteAction, (state, action) => {
145
- const actionName = getBookmarkBaseAction(action);
146
- state.status.delete(actionName);
147
- });
148
- });
149
- };