|
| 1 | +import React, { useEffect, useReducer } from "react"; |
| 2 | +import API from "../API"; |
| 3 | +import { InitialData } from "../data/Data"; |
| 4 | +import { |
| 5 | + ApplicationContext, |
| 6 | + initialApplicationContext |
| 7 | +} from "./ApplicationContext"; |
| 8 | + |
| 9 | +// Resort to session storage to persist session data |
| 10 | +// Use Context API as middleware to parse and easily manipulate |
| 11 | +// the data in session storage |
| 12 | +// https://stackoverflow.com/a/62505656/9744696 |
| 13 | +const ApplicationProvider = ({ children }) => { |
| 14 | + // Reducer types |
| 15 | + const reducerTypes = Object.freeze({ |
| 16 | + rdf: "rdf", |
| 17 | + addRdf: "addRdf", |
| 18 | + sparqlQuery: "sparql", |
| 19 | + sparqlEnpoint: "sparqlEndpoint", |
| 20 | + shex: "shex", |
| 21 | + shacl: "shacl", |
| 22 | + validationEndpoint: "validationEndpoint", |
| 23 | + shapeMap: "shapeMap", |
| 24 | + uml: "uml", |
| 25 | + }); |
| 26 | + |
| 27 | + // Reducer function |
| 28 | + function applicationDataReducer(state, { type, value }) { |
| 29 | + // Last trap for nullish values |
| 30 | + if (!value) return state; |
| 31 | + |
| 32 | + // Trim text and URLs before storing |
| 33 | + const trimBeforeStore = (item) => ({ |
| 34 | + ...item, |
| 35 | + textArea: item.textArea.trim(), |
| 36 | + url: item.url.trim(), |
| 37 | + }); |
| 38 | + |
| 39 | + const finalValue = Array.isArray(value) |
| 40 | + ? value.map(trimBeforeStore) |
| 41 | + : typeof value === "object" |
| 42 | + ? trimBeforeStore(value) |
| 43 | + : typeof value === "string" |
| 44 | + ? value.trim() |
| 45 | + : value; |
| 46 | + |
| 47 | + switch (type) { |
| 48 | + case reducerTypes.rdf: |
| 49 | + return { ...state, rdfData: finalValue }; |
| 50 | + case reducerTypes.addRdf: |
| 51 | + return { |
| 52 | + ...state, |
| 53 | + rdfData: [...state.rdfData, finalValue], |
| 54 | + }; |
| 55 | + case reducerTypes.sparqlQuery: |
| 56 | + return { ...state, sparqlQuery: finalValue }; |
| 57 | + case reducerTypes.sparqlEnpoint: |
| 58 | + return { ...state, sparqlEndpoint: finalValue }; |
| 59 | + case reducerTypes.shex: |
| 60 | + return { ...state, shexSchema: finalValue }; |
| 61 | + case reducerTypes.shacl: |
| 62 | + return { ...state, shaclSchema: finalValue }; |
| 63 | + case reducerTypes.validationEndpoint: |
| 64 | + return { ...state, validationEndpoint: finalValue }; |
| 65 | + case reducerTypes.shapeMap: |
| 66 | + return { ...state, shapeMap: finalValue }; |
| 67 | + case reducerTypes.uml: |
| 68 | + return { ...state, umlData: finalValue }; |
| 69 | + default: |
| 70 | + return state; |
| 71 | + } |
| 72 | + } |
| 73 | + |
| 74 | + // Reducer to handle the application data |
| 75 | + const [applicationData, dispatch] = useReducer( |
| 76 | + applicationDataReducer, |
| 77 | + getLocalStorage(API.sessionStorageDataKey, initialApplicationContext) |
| 78 | + ); |
| 79 | + |
| 80 | + function setLocalStorage(key, value) { |
| 81 | + try { |
| 82 | + window.localStorage.setItem(key, JSON.stringify(value)); |
| 83 | + } catch (e) { |
| 84 | + // catch possible errors |
| 85 | + } |
| 86 | + } |
| 87 | + |
| 88 | + function getLocalStorage(key, initialValue) { |
| 89 | + try { |
| 90 | + const value = window.localStorage.getItem(key); |
| 91 | + return value ? JSON.parse(value) : initialValue; |
| 92 | + } catch (e) { |
| 93 | + // if error, return initial value |
| 94 | + return initialValue; |
| 95 | + } |
| 96 | + } |
| 97 | + |
| 98 | + // Update session data when context data changed |
| 99 | + useEffect(() => { |
| 100 | + setLocalStorage(API.sessionStorageDataKey, applicationData); |
| 101 | + }, [applicationData]); |
| 102 | + |
| 103 | + return ( |
| 104 | + <ApplicationContext.Provider |
| 105 | + value={{ |
| 106 | + ...applicationData, |
| 107 | + // Granular control over the data to be updated |
| 108 | + setRdfData: (rdfData) => |
| 109 | + dispatch({ type: reducerTypes.rdf, value: rdfData }), |
| 110 | + addRdfData: (rdfData) => { |
| 111 | + const newIndex = applicationData.rdfData.length; // new Data index based on current context data |
| 112 | + // New data object, use InitialData if no data was provided |
| 113 | + const newData = { |
| 114 | + index: newIndex, |
| 115 | + ...(rdfData || InitialData), |
| 116 | + }; |
| 117 | + dispatch({ type: reducerTypes.addRdf, value: newData }); // Update state and return new data |
| 118 | + return newData; |
| 119 | + }, |
| 120 | + setSparqlQuery: (sparqlQuery) => |
| 121 | + dispatch({ type: reducerTypes.sparqlQuery, value: sparqlQuery }), |
| 122 | + setSparqlEndpoint: (sparqlEndpoint) => |
| 123 | + dispatch({ type: reducerTypes.sparqlEnpoint, value: sparqlEndpoint }), |
| 124 | + setShexSchema: (shexSchema) => |
| 125 | + dispatch({ type: reducerTypes.shex, value: shexSchema }), |
| 126 | + setShaclSchema: (shaclSchema) => |
| 127 | + dispatch({ type: reducerTypes.shacl, value: shaclSchema }), |
| 128 | + setValidationEndpoint: (vEndpoint) => |
| 129 | + dispatch({ type: reducerTypes.validationEndpoint, value: vEndpoint }), |
| 130 | + setShapeMap: (shapeMap) => |
| 131 | + dispatch({ type: reducerTypes.shapeMap, value: shapeMap }), |
| 132 | + setUmlData: (umlData) => |
| 133 | + dispatch({ type: reducerTypes.uml, value: umlData }), |
| 134 | + }} |
| 135 | + > |
| 136 | + {children} |
| 137 | + </ApplicationContext.Provider> |
| 138 | + ); |
| 139 | +}; |
| 140 | + |
| 141 | +export default ApplicationProvider; |
0 commit comments