Skip to content

Commit 4386cba

Browse files
authored
Merge pull request os2display#368 from os2display/feature/v3-fix-screen-saving
V3: Fix screen saving
2 parents cbe4232 + 6ad9c39 commit 4386cba

6 files changed

Lines changed: 68 additions & 67 deletions

File tree

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ All notable changes to this project will be documented in this file.
2828
- Aligned environment variable names.
2929
- Aligned with v. 2.6.0.
3030
- Added relations checksum feature flag.
31+
- Fixes saving issues described in issue where saving resulted in infinite spinner.
32+
- Fixed loading of routes containing null string values.
3133

3234
### NB! Prior to 3.x the project was split into separate repositories
3335

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/util/fetch-data-hook.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ function useFetchDataHook(apiCall, ids, params = {}, key = "id") {
1010
useEffect(() => {
1111
if (!ids || ids.length === 0) return;
1212

13+
// Filter out null/undefined/empty IDs
14+
const validIds = ids.filter((id) => id != null && id !== "");
15+
if (validIds.length === 0) return;
16+
1317
async function fetchItems() {
1418
setLoading(true);
1519

assets/admin/components/util/helpers/id-from-url.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
*/
66
function idFromUrl(string, index = 0) {
77
if (typeof string === "string") {
8-
return string.match(/[A-Za-z0-9]{26}/g)[index];
8+
return (string.match(/[A-Za-z0-9]{26}/g) ?? [])[index] ?? "";
99
}
1010
return "";
1111
}

0 commit comments

Comments
 (0)