Skip to content

Commit 89f1da1

Browse files
committed
API: Move notification dispatch to Image Builder API slice
This commit moves the notification dispatching for creating composes and clones into a more sensible location – the Image Builder API slice. It is more sensible because it separates the logic of the React component (the wizard or share images modal) from the logic of handling the request life cycle (which is now handled entirely in the slice). There is a subtle but significant change – a new request will be dispatched for every request. This is the correct way to do things as it is possible that some requests succeed, and that others fail. Insights causes the notifications to stack on top of each other neatly, so there is no UI problem. To facilitate this, we also need to use use Promise.allSettled instead of Promise.all.
1 parent b312e4a commit 89f1da1

File tree

3 files changed

+60
-79
lines changed

3 files changed

+60
-79
lines changed

src/Components/CreateImageWizard/CreateImageWizard.js

Lines changed: 4 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
import React from 'react';
22

33
import componentTypes from '@data-driven-forms/react-form-renderer/component-types';
4-
import { addNotification } from '@redhat-cloud-services/frontend-components-notifications/redux';
54
import { useFlag } from '@unleash/proxy-client-react';
6-
import { useDispatch, useStore } from 'react-redux';
75
import { useNavigate, useParams } from 'react-router-dom';
86

97
import ImageCreator from './ImageCreator';
@@ -533,7 +531,6 @@ const formStepHistory = (composeRequest, contentSourcesEnabled, isBeta) => {
533531

534532
const CreateImageWizard = () => {
535533
const [composeImage] = useComposeImageMutation();
536-
const dispatch = useDispatch();
537534
const navigate = useNavigate();
538535
// composeId is an optional param that is used for Recreate image
539536
const { composeId } = useParams();
@@ -596,39 +593,10 @@ const CreateImageWizard = () => {
596593
onSubmit={async ({ values, setIsSaving }) => {
597594
setIsSaving(true);
598595
const requests = onSave(values);
599-
// https://redux-toolkit.js.org/rtk-query/usage/mutations#frequently-used-mutation-hook-return-values
600-
// If you want to immediately access the result of a mutation, you need to chain `.unwrap()`
601-
// if you actually want the payload or to catch the error.
602-
// We do this so we can dispatch the appropriate notification (success or failure).
603-
await Promise.all(
604-
requests.map((composeRequest) =>
605-
composeImage({ composeRequest }).unwrap()
606-
)
607-
)
608-
.then(() => {
609-
navigate(resolveRelPath(''));
610-
dispatch(
611-
addNotification({
612-
variant: 'success',
613-
title: 'Your image is being created',
614-
})
615-
);
616-
})
617-
.catch((err) => {
618-
let msg = err.response.statusText;
619-
if (err.response.data?.errors[0]?.detail) {
620-
msg = err.response.data?.errors[0]?.detail;
621-
}
622-
623-
navigate(resolveRelPath(''));
624-
dispatch(
625-
addNotification({
626-
variant: 'danger',
627-
title: 'Your image could not be created',
628-
description: 'Status code ' + err.response.status + ': ' + msg,
629-
})
630-
);
631-
});
596+
await Promise.allSettled(
597+
requests.map((composeRequest) => composeImage({ composeRequest }))
598+
);
599+
navigate(resolveRelPath(''));
632600
}}
633601
defaultArch="x86_64"
634602
customValidatorMapper={{

src/Components/ShareImageModal/RegionsSelect.tsx

Lines changed: 2 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,6 @@ import {
1212
ValidatedOptions,
1313
} from '@patternfly/react-core';
1414
import { ExclamationCircleIcon, HelpIcon } from '@patternfly/react-icons';
15-
import { addNotification } from '@redhat-cloud-services/frontend-components-notifications/redux';
16-
import { useDispatch } from 'react-redux';
1715
import { useNavigate } from 'react-router-dom';
1816

1917
import { AWS_REGIONS } from '../../constants';
@@ -64,7 +62,6 @@ const RegionsSelect = ({
6462
isOpen,
6563
setIsOpen,
6664
}: RegionsSelectPropTypes) => {
67-
const dispatch = useDispatch();
6865
const navigate = useNavigate();
6966
const [isSaving, setIsSaving] = useState(false);
7067
const [selected, setSelected] = useState<string[]>([]);
@@ -113,32 +110,8 @@ const RegionsSelect = ({
113110
const handleSubmit = async () => {
114111
setIsSaving(true);
115112
const requests = generateRequests(composeId, composeStatus, selected);
116-
// https://redux-toolkit.js.org/rtk-query/usage/mutations#frequently-used-mutation-hook-return-values
117-
// If you want to immediately access the result of a mutation, you need to chain `.unwrap()`
118-
// if you actually want the payload or to catch the error.
119-
// We do this so we can dispatch the appropriate notification (success or failure).
120-
await Promise.all(requests.map((request) => cloneCompose(request).unwrap()))
121-
.then(() => {
122-
setIsSaving(false);
123-
navigate(resolveRelPath(''));
124-
dispatch(
125-
addNotification({
126-
variant: 'success',
127-
title: 'Your image is being shared',
128-
})
129-
);
130-
})
131-
.catch((err) => {
132-
navigate(resolveRelPath(''));
133-
// TODO The error should be typed.
134-
dispatch(
135-
addNotification({
136-
variant: 'danger',
137-
title: 'Your image could not be shared',
138-
description: `Status code ${err.status}: ${err.data.errors[0].detail}`,
139-
})
140-
);
141-
});
113+
await Promise.allSettled(requests.map((request) => cloneCompose(request)));
114+
navigate(resolveRelPath(''));
142115
};
143116

144117
return (

src/store/enhancedImageBuilderApi.ts

Lines changed: 54 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { addNotification } from '@redhat-cloud-services/frontend-components-notifications/redux';
2+
13
import { imageBuilderApi } from './imageBuilderApi';
24

35
const enhancedApi = imageBuilderApi.enhanceEndpoints({
@@ -18,27 +20,65 @@ const enhancedApi = imageBuilderApi.enhanceEndpoints({
1820
{ composeId, cloneRequest },
1921
{ dispatch, queryFulfilled }
2022
) => {
21-
queryFulfilled.then(() => {
22-
dispatch(
23-
imageBuilderApi.util.invalidateTags([
24-
// Typescript is unaware of tag types being defined concurrently in enhanceEndpoints()
25-
// @ts-expect-error
26-
{ type: 'Clone', id: composeId },
27-
])
28-
);
29-
});
23+
queryFulfilled
24+
.then(() => {
25+
dispatch(
26+
imageBuilderApi.util.invalidateTags([
27+
// Typescript is unaware of tag types being defined concurrently in enhanceEndpoints()
28+
// @ts-expect-error
29+
{ type: 'Clone', id: composeId },
30+
])
31+
);
32+
33+
dispatch(
34+
addNotification({
35+
variant: 'success',
36+
title: 'Your image is being shared',
37+
})
38+
);
39+
})
40+
.catch((err) => {
41+
dispatch(
42+
addNotification({
43+
variant: 'danger',
44+
title: 'Your image could not be shared',
45+
description: `Status code ${err.status}: ${err.data.errors[0].detail}`,
46+
})
47+
);
48+
});
3049
},
3150
},
3251
composeImage: {
3352
onQueryStarted: async (
3453
{ composeRequest },
3554
{ dispatch, queryFulfilled }
3655
) => {
37-
queryFulfilled.then(() => {
38-
// Typescript is unaware of tag types being defined concurrently in enhanceEndpoints()
39-
// @ts-expect-error
40-
dispatch(imageBuilderApi.util.invalidateTags(['Compose']));
41-
});
56+
queryFulfilled
57+
.then(() => {
58+
// Typescript is unaware of tag types being defined concurrently in enhanceEndpoints()
59+
// @ts-expect-error
60+
dispatch(imageBuilderApi.util.invalidateTags(['Compose']));
61+
dispatch(
62+
addNotification({
63+
variant: 'success',
64+
title: 'Your image is being created',
65+
})
66+
);
67+
})
68+
.catch((err) => {
69+
let msg = err.response.statusText;
70+
if (err.response.data?.errors[0]?.detail) {
71+
msg = err.response.data?.errors[0]?.detail;
72+
}
73+
74+
dispatch(
75+
addNotification({
76+
variant: 'danger',
77+
title: 'Your image could not be created',
78+
description: 'Status code ' + err.response.status + ': ' + msg,
79+
})
80+
);
81+
});
4282
},
4383
},
4484
},

0 commit comments

Comments
 (0)