Skip to content

Commit 2128826

Browse files
committed
6871: Fixed merge
2 parents 3778212 + 4386cba commit 2128826

30 files changed

Lines changed: 1440 additions & 848 deletions

.env

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ KEY_VAULT_SOURCE=ENVIRONMENT
6868
KEY_VAULT_JSON="{}"
6969
TRACK_SCREEN_INFO=false
7070
TRACK_SCREEN_INFO_UPDATE_INTERVAL_SECONDS=300
71+
RELATIONS_CHECKSUM_ENABLED=true
7172
###< App ###
7273

7374
###> itk-dev/openid-connect-bundle ###

CHANGELOG.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,15 @@ All notable changes to this project will be documented in this file.
2222
- Aligned with v. 2.5.2.
2323
- Removed themes.
2424
- Added command to migrate config.json files.
25-
- Fix data fetching bug + tests
25+
- Fix data fetching bug and tests
2626
- Refactored screen layout commands.
2727
- Updated infrastructure and image build for mono-repo
2828
- Moved list components (search and checkboxes) around.
2929
- Aligned environment variable names.
3030
- Aligned with v. 2.6.0.
31+
- Added relations checksum feature flag.
32+
- Fixes saving issues described in issue where saving resulted in infinite spinner.
33+
- Fixed loading of routes containing null string values.
3134

3235
### NB! Prior to 3.x the project was split into separate repositories
3336

assets/admin/components/screen/screen-form.jsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,6 @@ function ScreenForm({
206206
<FormInput
207207
name="location"
208208
type="text"
209-
required
210209
label={t("screen-location-label")}
211210
helpText={t("screen-location-placeholder")}
212211
value={screen.location}

assets/admin/components/screen/screen-manager.jsx

Lines changed: 59 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useEffect, useState } from "react";
1+
import { useEffect, useRef, useState } from "react";
22
import { useTranslation } from "react-i18next";
33
import { useNavigate } from "react-router-dom";
44
import {
@@ -13,6 +13,15 @@ import {
1313
import idFromUrl from "../util/helpers/id-from-url";
1414
import { set } from "lodash/object";
1515

16+
const orientationOptions = [
17+
{ title: "Vertikal", "@id": "vertical" },
18+
{ title: "Horisontal", "@id": "horizontal" },
19+
];
20+
const resolutionOptions = [
21+
{ title: "4K", "@id": "4K" },
22+
{ title: "HD", "@id": "HD" },
23+
];
24+
1625
/**
1726
* The screen manager component.
1827
*
@@ -34,16 +43,8 @@ function ScreenManager({
3443
initialState = null,
3544
}) {
3645
const { t } = useTranslation("common", { keyPrefix: "screen-manager" });
37-
const [saveWithoutClose, setSaveWithoutClose] = useState(false);
46+
const saveWithoutCloseRef = useRef(false);
3847
const navigate = useNavigate();
39-
const orientationOptions = [
40-
{ title: "Vertikal", "@id": "vertical" },
41-
{ title: "Horisontal", "@id": "horizontal" },
42-
];
43-
const resolutionOptions = [
44-
{ title: "4K", "@id": "4K" },
45-
{ title: "HD", "@id": "HD" },
46-
];
4748
const headerText =
4849
saveMethod === "PUT" ? t("edit-screen-header") : t("create-screen-header");
4950
const [loadingMessage, setLoadingMessage] = useState("");
@@ -61,14 +62,6 @@ function ScreenManager({
6162
{ data: postData, error: saveErrorPost, isSuccess: isSaveSuccessPost },
6263
] = usePostV2ScreensMutation();
6364

64-
/** If the screen is saved, display the success message */
65-
useEffect(() => {
66-
if (isSaveSuccessPost || isSaveSuccessPut) {
67-
displaySuccess(t("success-messages.saved-screen"));
68-
setSavingScreen(false);
69-
}
70-
}, [isSaveSuccessPost, isSaveSuccessPut]);
71-
7265
/** If the screen is saved with error, display the error message */
7366
useEffect(() => {
7467
if (saveErrorPut || saveErrorPost) {
@@ -78,14 +71,14 @@ function ScreenManager({
7871
);
7972
setSavingScreen(false);
8073
}
81-
}, [saveErrorPut, saveErrorPost]);
74+
}, [saveErrorPut, saveErrorPost, t]);
8275

8376
/** If the screen is not loaded, display the error message */
8477
useEffect(() => {
8578
if (loadingError) {
8679
displayError(t("error-messages.load-screen-error", { id }), loadingError);
8780
}
88-
}, [loadingError]);
81+
}, [loadingError, id, t]);
8982

9083
/**
9184
* Set state on change in input field
@@ -94,8 +87,7 @@ function ScreenManager({
9487
* @param {object} props.target - Event target.
9588
*/
9689
const handleInput = ({ target }) => {
97-
let localFormStateObject = { ...formStateObject };
98-
localFormStateObject = JSON.parse(JSON.stringify(localFormStateObject));
90+
const localFormStateObject = JSON.parse(JSON.stringify(formStateObject));
9991
set(localFormStateObject, target.id, target.value);
10092
setFormStateObject(localFormStateObject);
10193
};
@@ -126,9 +118,9 @@ function ScreenManager({
126118
*
127119
* @returns {Array | null} A mapped array with group ids or null
128120
*/
129-
function mapGroups() {
130-
if (formStateObject.inScreenGroups) {
131-
return formStateObject.inScreenGroups.map((group) => {
121+
function mapGroups(screenState) {
122+
if (screenState?.inScreenGroups instanceof Array) {
123+
return screenState.inScreenGroups.map((group) => {
132124
return idFromUrl(group);
133125
});
134126
}
@@ -142,11 +134,9 @@ function ScreenManager({
142134
* @returns {Array | null} A mapped array with playlist ids and weight
143135
* filtered by region id or null
144136
*/
145-
function getPlaylistsByRegionId(regionId) {
146-
const { playlists } = formStateObject;
147-
137+
function getPlaylistsByRegionId(playlists, regionId) {
148138
return playlists
149-
.filter(({ region }) => idFromUrl(region) === idFromUrl(regionId))
139+
.filter(({ region }) => region === regionId)
150140
.map((playlist, index) => {
151141
return { id: idFromUrl(playlist["@id"]), weight: index };
152142
});
@@ -157,8 +147,9 @@ function ScreenManager({
157147
* @param {Array} array The array to remove from.
158148
*/
159149
function removeFromArray(itemId, array) {
160-
if (array.indexOf(itemId) >= 0) {
161-
array.splice(array.indexOf(itemId), 1);
150+
const index = array.indexOf(itemId);
151+
if (index >= 0) {
152+
array.splice(index, 1);
162153
}
163154
}
164155

@@ -167,16 +158,19 @@ function ScreenManager({
167158
*
168159
* @returns {Array | null} A mapped array with playlist, regions and weight or null
169160
*/
170-
function mapPlaylistsWithRegion() {
161+
function mapPlaylistsWithRegion(playlists, regions) {
171162
const returnArray = [];
172-
const { playlists, regions } = formStateObject;
173-
const regionIds = regions.map((r) => idFromUrl(r["@id"]));
163+
const regionIds = regions.map((r) =>
164+
typeof r === "string" ? idFromUrl(r, 1) : idFromUrl(r["@id"]),
165+
);
174166

175167
// The playlists all have a regionId, the following creates a unique list of relevant regions If there are not
176168
// playlists, then an empty playlist is to be saved per region
177169
let playlistRegions = [];
178170
if (playlists?.length > 0) {
179-
playlistRegions = [...new Set(playlists.map(({ region }) => region))];
171+
playlistRegions = [
172+
...new Set(playlists.map(({ region }) => idFromUrl(region))),
173+
];
180174
}
181175

182176
// Then the playlists are mapped by region Looping through the regions that have a playlist connected...
@@ -187,19 +181,18 @@ function ScreenManager({
187181

188182
// Add regionsId and connected playlists to the returnarray
189183
returnArray.push({
190-
playlists: getPlaylistsByRegionId(regionId),
191-
regionId: idFromUrl(regionId),
184+
playlists: getPlaylistsByRegionId(playlists, regionId),
185+
regionId,
192186
});
193187
});
188+
194189
// The remaining regions are added with empty playlist arrays.
195-
if (regionIds.length > 0) {
196-
regionIds.forEach((regionId) =>
197-
returnArray.push({
198-
playlists: [],
199-
regionId: idFromUrl(regionId),
200-
}),
201-
);
202-
}
190+
regionIds.forEach((regionId) =>
191+
returnArray.push({
192+
playlists: [],
193+
regionId,
194+
}),
195+
);
203196
return returnArray;
204197
}
205198

@@ -208,25 +201,26 @@ function ScreenManager({
208201
*
209202
* @returns {string} Orientation or empty string
210203
*/
211-
function getOrientation() {
212-
const { orientation } = formStateObject;
213-
return orientation ? orientation[0]["@id"] : "";
204+
function getOrientation(screenState) {
205+
const { orientation } = screenState;
206+
return orientation && orientation.length > 0 ? orientation[0]["@id"] : "";
214207
}
215208

216209
/**
217210
* Gets resolution for submitting
218211
*
219212
* @returns {string} Resolution or empty string
220213
*/
221-
function getResolution() {
222-
const { resolution } = formStateObject;
214+
function getResolution(screenState) {
215+
const { resolution } = screenState;
223216
return resolution && resolution.length > 0 ? resolution[0]["@id"] : "";
224217
}
225218

226219
/** Handles submit. */
227220
const handleSubmit = () => {
228221
setSavingScreen(true);
229222
setLoadingMessage(t("loading-messages.saving-screen"));
223+
230224
const localFormStateObject = JSON.parse(JSON.stringify(formStateObject));
231225
const {
232226
title,
@@ -249,34 +243,36 @@ function ScreenManager({
249243
layout,
250244
location,
251245
enableColorSchemeChange,
252-
resolution: getResolution(),
253-
groups: mapGroups(),
254-
orientation: getOrientation(),
255-
regions: mapPlaylistsWithRegion(),
246+
resolution: getResolution(localFormStateObject),
247+
groups: mapGroups(localFormStateObject),
248+
orientation: getOrientation(localFormStateObject),
249+
regions: mapPlaylistsWithRegion(
250+
localFormStateObject.playlists,
251+
localFormStateObject.regions,
252+
),
256253
}),
257254
};
258255

259-
setLoadingMessage(t("loading-messages.saving-screen"));
260-
261256
if (saveMethod === "POST") {
262257
PostV2Screens(saveData);
263258
} else if (saveMethod === "PUT") {
264259
PutV2Screens({ ...saveData, id });
265260
}
266261
};
267262

268-
const handleSubmitWithRedirect = () => {
269-
setSaveWithoutClose(true);
263+
const handleSaveAndStay = () => {
264+
saveWithoutCloseRef.current = true;
270265
handleSubmit();
271266
};
272267

273268
/** Handle submitting is done. */
274269
useEffect(() => {
275-
if (isSaveSuccessPut || isSaveSuccessPost) {
270+
if (isSaveSuccessPost || isSaveSuccessPut) {
271+
displaySuccess(t("success-messages.saved-screen"));
276272
setSavingScreen(false);
277273

278-
if (saveWithoutClose) {
279-
setSaveWithoutClose(false);
274+
if (saveWithoutCloseRef.current) {
275+
saveWithoutCloseRef.current = false;
280276

281277
if (isSaveSuccessPost) {
282278
navigate(`/screen/edit/${idFromUrl(postData["@id"])}`);
@@ -285,13 +281,13 @@ function ScreenManager({
285281
navigate("/screen/list");
286282
}
287283
}
288-
}, [isSaveSuccessPut, isSaveSuccessPost]);
284+
}, [isSaveSuccessPut, isSaveSuccessPost, postData, navigate, t]);
289285

290286
return (
291287
<>
292288
{formStateObject && (
293289
<ScreenForm
294-
handleSubmitWithoutRedirect={handleSubmitWithRedirect}
290+
handleSubmitWithoutRedirect={handleSaveAndStay}
295291
screen={formStateObject}
296292
orientationOptions={orientationOptions}
297293
resolutionOptions={resolutionOptions}

assets/admin/components/screen/util/campaign-icon.jsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,10 @@ function CampaignIcon({ id, delay = 1000 }) {
2828

2929
const { data: campaigns, isLoading } = useGetV2ScreensByIdCampaignsQuery(
3030
{ id },
31-
{ skip: !getData },
31+
{ skip: !getData || !id },
3232
);
3333
const { data: groups, isLoading: isLoadingScreenGroups } =
34-
useGetV2ScreensByIdScreenGroupsQuery({ id }, { skip: !getData });
34+
useGetV2ScreensByIdScreenGroupsQuery({ id }, { skip: !getData || !id });
3535

3636
useEffect(() => {
3737
if (campaigns) {

assets/admin/components/slide/slide-manager.jsx

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { ulid } from "ulid";
44
import { useDispatch } from "react-redux";
55
import dayjs from "dayjs";
66
import { useNavigate } from "react-router-dom";
7+
import rebuildMediaFromContent from "./slide-media-utils";
78
import UserContext from "../../context/user-context";
89
import {
910
enhancedApi,
@@ -337,13 +338,11 @@ function SlideManager({
337338
const localFormStateObject = { ...formStateObject };
338339
const localMediaData = { ...mediaData };
339340
// Set field as a field to look into for new references.
340-
setMediaFields([...new Set([...mediaFields, fieldId])]);
341+
const updatedMediaFields = [...new Set([...mediaFields, fieldId])];
342+
setMediaFields(updatedMediaFields);
341343

342344
const newField = [];
343345

344-
if (Array.isArray(fieldValue) && fieldValue.length === 0) {
345-
localFormStateObject.media = [];
346-
}
347346
// Handle each entry in field.
348347
if (Array.isArray(fieldValue)) {
349348
fieldValue.forEach((entry) => {
@@ -383,17 +382,19 @@ function SlideManager({
383382
!Object.prototype.hasOwnProperty.call(localMediaData, entry["@id"])
384383
) {
385384
set(localMediaData, entry["@id"], entry);
386-
387-
localFormStateObject.media.push(entry["@id"]);
388385
}
389386
}
390387
});
391388
}
392389

393390
set(localFormStateObject.content, fieldId, newField);
394-
set(localFormStateObject, "media", [
395-
...new Set([...localFormStateObject.media]),
396-
]);
391+
392+
// Rebuild the media array from all content fields to keep it in sync.
393+
set(
394+
localFormStateObject,
395+
"media",
396+
rebuildMediaFromContent(localFormStateObject.content),
397+
);
397398

398399
setFormStateObject(localFormStateObject);
399400
setMediaData(localMediaData);

0 commit comments

Comments
 (0)