diff --git a/src/Components/Main/Home/Components/GridCards.js b/src/Components/Main/Home/Components/GridCards.js
index 83a603b..aa1fb0c 100644
--- a/src/Components/Main/Home/Components/GridCards.js
+++ b/src/Components/Main/Home/Components/GridCards.js
@@ -1,74 +1,14 @@
-import React, { useState, useEffect, useCallback } from "react";
-import isElectron from "is-electron";
-
//Components
import Card from "../../../SharedComponents/Card/Card.js";
import Spinner from "../../../SharedComponents/Spinner/Spinner.js";
import ProgressPie from "../../../SharedComponents/Card/Components/ProgressPie.jsx";
-const GridCards = () => {
- const [modules, setModules] = useState([]);
- const [loading, setLoading] = useState(true);
-
- //Get the modules from the localStorage and set the module state
- //Updates every time localeStorage changes
- //TODO getting the public folder is blocking the function on no internet connection
- const modulesFromBrowserStorage = useCallback(async () => {
- //The question types folder from the public folder needs to be combined
- //with every module from the locale storage
-
- try {
- //Get the data from the public folder
- const publicFolder = await fetch("data.json", { mode: "no-cors" });
- const resJSON = await publicFolder.json();
-
- //Get modules from the localStorage
- let storageModules = [];
- Object.entries(localStorage).forEach((key) => {
- if (key[0].startsWith("repeatio-module")) {
- const module = localStorage.getItem(key[0]);
- storageModules.push(JSON.parse(module));
- }
- });
+//Hooks
+import useAllModules from "../hooks/useAllModules.js";
- //Update the state
- setModules([...storageModules, resJSON]);
- setLoading(false);
- } catch (error) {
- console.log(error.message);
- }
- }, []);
-
- //Refetch the modules if the localeStorage changes
- const onStorageChange = useCallback(() => {
- setLoading(true);
- modulesFromBrowserStorage();
- }, [modulesFromBrowserStorage]);
-
- //Fetch data for all modules by reading all repeatio files in documents folder / locale storage (in browser)
- useEffect(() => {
- if (isElectron()) {
- // Send a message to the main process
- window.api.request("toMain", ["getModules"]);
-
- // Called when message received from main process
- window.api.response("fromMain", (data) => {
- setModules(data);
- setLoading(false);
- });
- } else {
- //Get modules from localStorage and add storage onChange handler
- modulesFromBrowserStorage();
- window.addEventListener("storage", onStorageChange);
- }
-
- //Reset the modules and remove the handler when the component unmounts
- return () => {
- setModules([]);
- setLoading(true);
- if (!isElectron()) window.removeEventListener("storage", onStorageChange);
- };
- }, [modulesFromBrowserStorage, onStorageChange]);
+//Component
+const GridCards = () => {
+ const { modules, loading } = useAllModules();
//Display loading spinner while component loads
//TODO switch to suspense maybe (react 18)
@@ -78,27 +18,38 @@ const GridCards = () => {
//Return grid of modules and "add module" card when the component has loaded
return (
-
- {modules.map((module) => {
- const { id, name, questions, disabled } = module;
- return (
- }
- leftBottom={{
- type: "link",
- linkTo: `/module/${id}`,
- linkAriaLabel: `View ${name}`,
- linkText: "View",
- }}
- />
- );
- })}
-
+ <>
+
+ {modules.map((module) => {
+ const { id, name, questions, disabled } = module;
+ return (
+ }
+ leftBottom={{
+ type: "link",
+ linkTo: `/module/${id}`,
+ linkAriaLabel: `View ${name}`,
+ linkText: "View",
+ }}
+ />
+ );
+ })}
+
+ {/*
+ //TODO display errors with react-toastify
+ //Need to check length of array?
+
+ {errors &&
+ errors.map((error, index) => {
+ return
{error}
;
+ })}
+
*/}
+ >
);
};
diff --git a/src/Components/Main/Home/Components/ImportModule/CreateModule/CreateModule.jsx b/src/Components/Main/Home/Components/ImportModule/CreateModule/CreateModule.jsx
index 1a5d1ac..66b3ca8 100644
--- a/src/Components/Main/Home/Components/ImportModule/CreateModule/CreateModule.jsx
+++ b/src/Components/Main/Home/Components/ImportModule/CreateModule/CreateModule.jsx
@@ -26,7 +26,7 @@ const CreateModule = ({ handleModalClose }) => {
//Right now it just replaces the old module in the localStorage with the uploaded one. */
//Update localeStorage and tell the window that a new storage event occurred
- localStorage.setItem(`repeatio-module-${module.id}`, JSON.stringify(module), {
+ localStorage.setItem(`repeatio-module-${module.id}`, JSON.stringify(module, null, "\t"), {
sameSite: "strict",
secure: true,
});
diff --git a/src/Components/Main/Home/Components/ImportModule/ImportModule.jsx b/src/Components/Main/Home/Components/ImportModule/ImportModule.jsx
index a316407..e08e894 100644
--- a/src/Components/Main/Home/Components/ImportModule/ImportModule.jsx
+++ b/src/Components/Main/Home/Components/ImportModule/ImportModule.jsx
@@ -83,7 +83,10 @@ const ImportModule = ({ handleModalClose }) => {
const data = await file.text();
//Update localeStorage and tell the window that a new storage event occurred
- localStorage.setItem(`repeatio-module-${JSON.parse(data).id}`, data, { sameSite: "strict", secure: true });
+ localStorage.setItem(`repeatio-module-${JSON.parse(data).id}`, data, {
+ sameSite: "strict",
+ secure: true,
+ });
window.dispatchEvent(new Event("storage"));
} catch (error) {
console.error(error);
diff --git a/src/Components/Main/Home/hooks/useAllModules.js b/src/Components/Main/Home/hooks/useAllModules.js
new file mode 100644
index 0000000..5a59122
--- /dev/null
+++ b/src/Components/Main/Home/hooks/useAllModules.js
@@ -0,0 +1,83 @@
+import { useState, useEffect, useCallback } from "react";
+
+//Functions
+import isElectron from "is-electron";
+import fetchModuleFromPublicFolder from "../../../../functions/fetchModuleFromPublicFolder.js";
+
+// Return the whole localStorage
+const useAllModules = () => {
+ const [loading, setLoading] = useState(true);
+ const [modules, setModules] = useState([]);
+ const [errors, setErrors] = useState([]);
+
+ //Get the modules from the localStorage and set the module state
+ //Updates every time localeStorage changes
+ const modulesFromBrowserStorage = useCallback(async () => {
+ //Setup variables for the module and possible errors
+ let localStorageModules = [];
+ let moduleErrors = [];
+
+ Object.entries(localStorage).forEach((key) => {
+ if (key[0].startsWith("repeatio-module")) {
+ //Get item, transform to object, on error add to moduleErrors array
+ try {
+ const module = localStorage.getItem(key[0]);
+ localStorageModules.push(JSON.parse(module));
+ } catch (error) {
+ console.warn(`${key[0]}: ${error.message}`);
+ moduleErrors.push(`${key[0]}: ${error.message}`);
+ }
+ }
+ });
+
+ //get the data from the public folder (types_1)
+ const dataFromPublicFolder = await fetchModuleFromPublicFolder();
+
+ //When able to fetch the data from the public folder, combine them else just show localStorage.
+ //This is useful for when the user is offline
+ if (dataFromPublicFolder !== undefined) {
+ setModules([...localStorageModules, dataFromPublicFolder]);
+ } else {
+ setModules(localStorageModules);
+ }
+
+ //Update states
+ setErrors(moduleErrors);
+ setLoading(false);
+ }, []);
+
+ //Refetch the modules if the localeStorage changes
+ const onStorageChange = useCallback(() => {
+ setLoading(true);
+ modulesFromBrowserStorage();
+ }, [modulesFromBrowserStorage]);
+
+ //Fetch data for all modules by reading all repeatio files in documents folder / locale storage (in browser)
+ useEffect(() => {
+ if (isElectron()) {
+ // Send a message to the main process
+ window.api.request("toMain", ["getModules"]);
+
+ // Called when message received from main process
+ window.api.response("fromMain", (data) => {
+ setModules(data);
+ setLoading(false);
+ });
+ } else {
+ //Get modules from localStorage and add storage onChange handler
+ modulesFromBrowserStorage();
+ window.addEventListener("storage", onStorageChange);
+ }
+
+ //Reset the modules and remove the handler when the component unmounts
+ return () => {
+ setModules([]);
+ setLoading(true);
+ if (!isElectron()) window.removeEventListener("storage", onStorageChange);
+ };
+ }, [modulesFromBrowserStorage, onStorageChange]);
+
+ return { modules, loading, errors };
+};
+
+export default useAllModules;
diff --git a/src/Context/ModuleContext.js b/src/Context/ModuleContext.js
index 7f25ea0..4be8cb0 100644
--- a/src/Context/ModuleContext.js
+++ b/src/Context/ModuleContext.js
@@ -1,5 +1,8 @@
import { createContext, useMemo, useState, useEffect, useCallback } from "react";
+
+//Functions
import isElectron from "is-electron";
+import fetchModuleFromPublicFolder from "../functions/fetchModuleFromPublicFolder.js";
//Create Question Context
export const ModuleContext = createContext([]);
@@ -20,36 +23,25 @@ export const ModuleProvider = (props) => {
//TODO move setFilteredQuestions/setInitialData into a callback
- // Callback
- //All questions
+ //Get the module data from the localStorage of the browser
const getDataFromBrowser = useCallback(async () => {
- //Fetch data from public folder
- let dataFromPublic;
- try {
- const data = await fetch("data.json", { mode: "no-cors" });
- dataFromPublic = await data.json();
- } catch (error) {
- console.log(error);
- }
+ let module;
- //Fetch data from the locale Storage
- let storageModules = [];
- Object.entries(localStorage).forEach((key) => {
- if (key[0].startsWith("repeatio-module")) {
- const repeatioModule = localStorage.getItem(key[0]);
- storageModules.push(JSON.parse(repeatioModule));
+ if (moduleContextID !== "types_1") {
+ //Fetch data from the locale Storage
+ try {
+ module = JSON.parse(localStorage.getItem(`repeatio-module-${moduleContextID}`));
+ } catch (error) {
+ console.warn(error.message);
}
- });
-
- //Combine the data from the public folder and the locale storage
- const modules = [...storageModules, dataFromPublic];
-
- //Find the correct module with the contextID
- const correctModule = modules.find((module) => module.id === moduleContextID);
+ } else {
+ //Fetch data from public folder
+ module = await fetchModuleFromPublicFolder();
+ }
//Set the data
- setInitialData(correctModule);
- setFilteredQuestions(correctModule.questions);
+ setInitialData(module);
+ setFilteredQuestions(module?.questions);
}, [moduleContextID]);
//Get all Questions from the file system / locale storage and provide them
@@ -81,7 +73,7 @@ export const ModuleProvider = (props) => {
//Update the localStorage/filesystem if initialData changes
useEffect(() => {
//Don't update the storage if the data is undefined or from the public folder (id: types_1)
- if (initialData === undefined || initialData.length < 1 || initialData.id === "types_1") {
+ if (initialData === undefined || initialData?.length < 1 || initialData?.id === "types_1") {
return;
}
diff --git a/src/functions/fetchModuleFromPublicFolder.js b/src/functions/fetchModuleFromPublicFolder.js
new file mode 100644
index 0000000..fcfa0a9
--- /dev/null
+++ b/src/functions/fetchModuleFromPublicFolder.js
@@ -0,0 +1,16 @@
+export default async function fetchModuleFromPublicFolder() {
+ try {
+ const data = await fetch("data.json", { mode: "no-cors" });
+ const toJsObject = await data.json();
+
+ if (data.ok) {
+ return toJsObject;
+ } else {
+ console.warn(toJsObject.message || toJsObject.statusText);
+ return;
+ }
+ } catch (error) {
+ console.warn(error.message);
+ return;
+ }
+}