From 1a7ad3bcba3cad8401328009dde7782e8fc1ba15 Mon Sep 17 00:00:00 2001 From: ivan Date: Tue, 14 Jun 2022 23:54:18 +0200 Subject: [PATCH 01/28] feat: add Dialog to load thing model from a server (Emporio) --- src/components/App/AppHeader/AppHeader.jsx | 7 + src/components/Dialogs/LoadTmDialog.jsx | 151 +++++++++++++++++++++ 2 files changed, 158 insertions(+) create mode 100644 src/components/Dialogs/LoadTmDialog.jsx diff --git a/src/components/App/AppHeader/AppHeader.jsx b/src/components/App/AppHeader/AppHeader.jsx index dbc2650..59b0951 100644 --- a/src/components/App/AppHeader/AppHeader.jsx +++ b/src/components/App/AppHeader/AppHeader.jsx @@ -20,6 +20,7 @@ import Button from "./Button"; import { ShareDialog } from "../../Dialogs/ShareDialog"; import { ConvertTmDialog } from "../../Dialogs/ConvertTmDialog"; import { CreateTdDialog } from "../../Dialogs/CreateTdDialog"; +import { LoadTmDialog } from "../../Dialogs/LoadTmDialog"; import { getFileHandle, getFileHTML5, _readFileHTML5 } from "../../../util.js"; @@ -306,6 +307,10 @@ export default function AppHeader() { const createTdDialog = React.useRef(); const openCreateTdDialog = () => { createTdDialog.current.openModal() } + const loadTmDialog = React.useRef(); + const openLoadTmDialog = () => { loadTmDialog.current.openModal() } + + return ( <>
@@ -320,6 +325,7 @@ export default function AppHeader() { + {(hasNativeFS()) && } {(context.showConvertBtn || context.isThingModel) && } @@ -328,6 +334,7 @@ export default function AppHeader() { + { + const context = useContext(ediTDorContext); + const [display, setDisplay] = React.useState(() => { + return false; + }); + const [thingModels, setThingModels] = React.useState([]); + const [choosenModel, setChoosenModel] = React.useState( + [] + ); + + useEffect(() => { + const getThingModels = async () => { + const tmsFromServer = await fetchThingModels(); + setThingModels(tmsFromServer); + }; + getThingModels(); + }, []); + + const fetchThingModels = async () => { + const res = await fetch(`${emporioUrl}/models`); + const data = await res.json(); + return data; + }; + + useImperativeHandle(ref, () => { + return { + openModal: () => open(), + close: () => close(), + }; + }); + + const open = () => { + setDisplay(true); + }; + + const close = () => { + setDisplay(false); + }; + + const content = buildForm(thingModels, setChoosenModel); + + if (display) { + return ReactDOM.createPortal( + { + let thingModel = choosenModel; + let linkedTd = {}; + linkedTd[thingModel["title"]] = thingModel; + context.updateLinkedTd(undefined); + context.addLinkedTd(linkedTd); + context.updateShowConvertBtn(true); + context.updateOfflineTD( + JSON.stringify(thingModel, null, "\t"), + "AppHeader" + ); + close(); + }} + children={content} + submitText={"Load TM"} + title={"Load new TM"} + description={ + "Choose a template Thing Model to load" + } + />, + document.getElementById("modal-root") + ); + } + + return null; +}); + +const buildForm = (thingModels, setChoosenModel) => { + return ( + <> +
+ {thingModels.map((thingModel, index) => ( + + ))} + + ); +}; + +const ThingModel = ({ thingModel, setChoosenModel }) => { + return ( +
{ + console.log(`choosen model is ${thingModel.title}`); + setChoosenModel(thingModel); + }} + > +

+ {thingModel.title} {thingModel["@type"]} +

+

{thingModel.description}

+
+ ); +}; + +const formField = ( + label, + placeholder, + id, + type, + autoFocus +) => { + return ( +
+ + +
+ ); +}; From f3376ca96080e51b87359c2628c07c735f026836 Mon Sep 17 00:00:00 2001 From: ivan Date: Wed, 15 Jun 2022 20:27:11 +0200 Subject: [PATCH 02/28] feat: improve load thing model modal --- src/assets/main.css | 13 ++ src/components/Dialogs/LoadTmDialog.jsx | 172 ++++++++++++++++++------ 2 files changed, 141 insertions(+), 44 deletions(-) diff --git a/src/assets/main.css b/src/assets/main.css index 7212f3f..aac1f7b 100644 --- a/src/assets/main.css +++ b/src/assets/main.css @@ -629,6 +629,11 @@ video { border-color: rgba(0, 90, 156, var(--tw-border-opacity)); } +.border-blue-500 { + --tw-border-opacity: 1; + border-color: rgba(0, 90, 156, var(--tw-border-opacity)); +} + .rounded { border-radius: 0.25rem; } @@ -1129,6 +1134,14 @@ video { width: 33.333333%; } +.w-1\/4 { + width: 25%; +} + +.w-7\/10 { + width: 70%; +} + .w-5\/12 { width: 41.666667%; } diff --git a/src/components/Dialogs/LoadTmDialog.jsx b/src/components/Dialogs/LoadTmDialog.jsx index e81e493..6f66a64 100644 --- a/src/components/Dialogs/LoadTmDialog.jsx +++ b/src/components/Dialogs/LoadTmDialog.jsx @@ -19,7 +19,8 @@ import React, { import ReactDOM from "react-dom"; import { DialogTemplate } from "./DialogTemplate"; import ediTDorContext from "../../context/ediTDorContext"; -import { ChevronDown } from "react-feather"; +import { ChevronDown, Search } from "react-feather"; + //TODO move to .env const emporioUrl = `http://localhost:3003`; @@ -33,20 +34,6 @@ export const LoadTmDialog = forwardRef((props, ref) => { [] ); - useEffect(() => { - const getThingModels = async () => { - const tmsFromServer = await fetchThingModels(); - setThingModels(tmsFromServer); - }; - getThingModels(); - }, []); - - const fetchThingModels = async () => { - const res = await fetch(`${emporioUrl}/models`); - const data = await res.json(); - return data; - }; - useImperativeHandle(ref, () => { return { openModal: () => open(), @@ -54,15 +41,52 @@ export const LoadTmDialog = forwardRef((props, ref) => { }; }); - const open = () => { + const open = async () => { + setThingModels(await fetchThingModels()); setDisplay(true); }; const close = () => { setDisplay(false); }; + const setSelectedThingModel = (index) => { + setThingModels( + thingModels.map((thing, thingIndex) => { + if (thingIndex === index) thing.selected = true; + else thing.selected = false; + return thing; + }) + ); + }; + + const fetchThingModels = async () => { + const res = await fetch(`${emporioUrl}/models`); + const data = await res.json(); + return data; + }; - const content = buildForm(thingModels, setChoosenModel); + const searchThingModels = async () => { + const attribute = + document.getElementById("search-option").value; + const searchText = + document.getElementById("search-id").value; + if (searchText === "") { + return setThingModels(await fetchThingModels()); + } + console.log(attribute); + console.log(searchText); + const res = await fetch( + `${emporioUrl}/models?${attribute}=${searchText}` + ); + setThingModels(await res.json()); + }; + + const content = buildForm( + thingModels, + setChoosenModel, + setSelectedThingModel, + searchThingModels + ); if (display) { return ReactDOM.createPortal( @@ -70,10 +94,10 @@ export const LoadTmDialog = forwardRef((props, ref) => { onCancel={close} onSubmit={() => { let thingModel = choosenModel; - let linkedTd = {}; - linkedTd[thingModel["title"]] = thingModel; + let linkedModel = {}; + linkedModel[thingModel["title"]] = thingModel; context.updateLinkedTd(undefined); - context.addLinkedTd(linkedTd); + context.addLinkedTd(linkedModel); context.updateShowConvertBtn(true); context.updateOfflineTD( JSON.stringify(thingModel, null, "\t"), @@ -95,49 +119,101 @@ export const LoadTmDialog = forwardRef((props, ref) => { return null; }); -const buildForm = (thingModels, setChoosenModel) => { +const buildForm = ( + thingModels, + setChoosenModel, + setSelectedThingModel, + searchThingModels +) => { return ( <> -
+
+
+ +
+ +
+
+ + + +
{thingModels.map((thingModel, index) => ( - + ))} ); }; -const ThingModel = ({ thingModel, setChoosenModel }) => { +const ThingModel = ({ + thingModel, + index, + setChoosenModel, + setSelectedThingModel, +}) => { + const types = formatThingModeltypes(thingModel["@type"]); return (
{ - console.log(`choosen model is ${thingModel.title}`); + className={`thingModel + my-1 + p-1 + bg-gray-600 + border-2 + rounded w-full + text-white + ${ + thingModel.selected + ? "border-blue-500" + : "border-gray-600" + }`} + onClick={() => { + setSelectedThingModel(index); setChoosenModel(thingModel); }} > -

- {thingModel.title} {thingModel["@type"]} -

-

{thingModel.description}

+
+

{thingModel.title}

+
+ {types ? types.map((type) =>

{type}

) : ""} +
+
+

+ {thingModel.description} +

); }; -const formField = ( - label, - placeholder, - id, - type, - autoFocus -) => { +const formField = (placeholder, id, type, autoFocus) => { return (
- ); }; + +const formatThingModeltypes = (type) => { + if (Array.isArray(type)) { + return type.filter( + (element) => element !== "tm:ThingModel" + ); + } +}; From 79f397a0967b391500c0a74c9010a3a199cde6f8 Mon Sep 17 00:00:00 2001 From: ivan Date: Thu, 16 Jun 2022 12:59:02 +0200 Subject: [PATCH 03/28] feat: add pagination --- src/components/Dialogs/LoadTmDialog.jsx | 115 +++++++++++++++++------- 1 file changed, 81 insertions(+), 34 deletions(-) diff --git a/src/components/Dialogs/LoadTmDialog.jsx b/src/components/Dialogs/LoadTmDialog.jsx index 6f66a64..e0d0448 100644 --- a/src/components/Dialogs/LoadTmDialog.jsx +++ b/src/components/Dialogs/LoadTmDialog.jsx @@ -12,14 +12,17 @@ ********************************************************************************/ import React, { forwardRef, - useEffect, useImperativeHandle, useContext, } from "react"; import ReactDOM from "react-dom"; import { DialogTemplate } from "./DialogTemplate"; import ediTDorContext from "../../context/ediTDorContext"; -import { ChevronDown, Search } from "react-feather"; +import { + ChevronDown, + ChevronRight, + ChevronLeft, +} from "react-feather"; //TODO move to .env const emporioUrl = `http://localhost:3003`; @@ -33,6 +36,11 @@ export const LoadTmDialog = forwardRef((props, ref) => { const [choosenModel, setChoosenModel] = React.useState( [] ); + const [pagination, setPagination] = React.useState({ + currentPage: 0, + loading: null, + thingModelsPerPage: 5, + }); useImperativeHandle(ref, () => { return { @@ -43,6 +51,7 @@ export const LoadTmDialog = forwardRef((props, ref) => { const open = async () => { setThingModels(await fetchThingModels()); + setPagination({ ...pagination, loading: false }); setDisplay(true); }; @@ -59,33 +68,66 @@ export const LoadTmDialog = forwardRef((props, ref) => { ); }; - const fetchThingModels = async () => { - const res = await fetch(`${emporioUrl}/models`); + const fetchThingModels = async ( + page = 0, + attribute = false, + searchText + ) => { + const offset = pagination.thingModelsPerPage * page; + let url = `${emporioUrl}/models?limit=${pagination.thingModelsPerPage}&offset=${offset}`; + if (attribute) url += `&${attribute}=${searchText}`; + const res = await fetch(url); const data = await res.json(); return data; }; - const searchThingModels = async () => { + const paginate = async (direction) => { + const page = + direction === "right" + ? pagination.currentPage + 1 + : pagination.currentPage - 1; + const searchText = + document.getElementById("search-id").value; const attribute = document.getElementById("search-option").value; + if (page < 0) return; + const thingModels = + searchText === "" + ? await fetchThingModels(page) + : await fetchThingModels( + page, + attribute, + searchText + ); + if (thingModels.length <= 0) return; + setThingModels(thingModels); + setPagination({ ...pagination, currentPage: page }); + }; + + const searchThingModels = async (page = 0) => { const searchText = document.getElementById("search-id").value; - if (searchText === "") { - return setThingModels(await fetchThingModels()); - } - console.log(attribute); - console.log(searchText); - const res = await fetch( - `${emporioUrl}/models?${attribute}=${searchText}` - ); - setThingModels(await res.json()); + const attribute = + document.getElementById("search-option").value; + setPagination({ ...pagination, currentPage: 0 }); + return searchText === "" + ? setThingModels(await fetchThingModels(page)) + : setThingModels( + await fetchThingModels( + page, + attribute, + searchText + ) + ); }; const content = buildForm( thingModels, + pagination.currentPage, setChoosenModel, setSelectedThingModel, - searchThingModels + searchThingModels, + paginate ); if (display) { @@ -93,14 +135,13 @@ export const LoadTmDialog = forwardRef((props, ref) => { { - let thingModel = choosenModel; let linkedModel = {}; - linkedModel[thingModel["title"]] = thingModel; + linkedModel[choosenModel["title"]] = choosenModel; context.updateLinkedTd(undefined); context.addLinkedTd(linkedModel); context.updateShowConvertBtn(true); context.updateOfflineTD( - JSON.stringify(thingModel, null, "\t"), + JSON.stringify(choosenModel, null, "\t"), "AppHeader" ); close(); @@ -121,9 +162,11 @@ export const LoadTmDialog = forwardRef((props, ref) => { const buildForm = ( thingModels, + page, setChoosenModel, setSelectedThingModel, - searchThingModels + searchThingModels, + paginate ) => { return ( <> @@ -168,6 +211,25 @@ const buildForm = ( setSelectedThingModel={setSelectedThingModel} /> ))} + +
+ + + {" "} + {page + 1}{" "} + + +
); }; @@ -211,21 +273,6 @@ const ThingModel = ({ ); }; -const formField = (placeholder, id, type, autoFocus) => { - return ( -
- -
- ); -}; - const formatThingModeltypes = (type) => { if (Array.isArray(type)) { return type.filter( From 4481876697e79960467b52dbb0e6a67fab378d94 Mon Sep 17 00:00:00 2001 From: ivan Date: Thu, 16 Jun 2022 15:59:00 +0200 Subject: [PATCH 04/28] fix: adequate selector and class to React conventions --- src/components/Dialogs/LoadTmDialog.jsx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/components/Dialogs/LoadTmDialog.jsx b/src/components/Dialogs/LoadTmDialog.jsx index e0d0448..c3dd6f4 100644 --- a/src/components/Dialogs/LoadTmDialog.jsx +++ b/src/components/Dialogs/LoadTmDialog.jsx @@ -175,10 +175,11 @@ const buildForm = (
@@ -194,7 +195,7 @@ const buildForm = ( - {" "} - {page + 1}{" "} + {" "}{page + 1}{" "} - {" "}{page + 1}{" "} + {" "} + {page + 1}{" "}
- {thingModels.map((thingModel, index) => ( + {thingModelObjects.map((thingModelObject, index) => ( @@ -233,12 +247,14 @@ const buildForm = ( }; const ThingModel = ({ - thingModel, + thingModelObject, index, setChoosenModel, setSelectedThingModel, }) => { - const types = formatThingModeltypes(thingModel["@type"]); + const types = formatThingModeltypes( + thingModelObject.thingModel["@type"] + ); return (
{ setSelectedThingModel(index); - setChoosenModel(thingModel); + setChoosenModel(thingModelObject.thingModel); }} >
-

{thingModel.title}

+

+ {thingModelObject.thingModel.title} +

{types ? types.map((type) =>

{type}

) : ""}

- {thingModel.description} + {thingModelObject.thingModel.description}

); From b148041c050254870df588830623a77d8ffaa5bd Mon Sep 17 00:00:00 2001 From: ivan Date: Tue, 28 Jun 2022 16:40:00 +0200 Subject: [PATCH 08/28] feat: add input to change Emporio URL thorugh the GUI --- src/components/Dialogs/LoadTmDialog.jsx | 106 ++++++++++++++++++++---- 1 file changed, 88 insertions(+), 18 deletions(-) diff --git a/src/components/Dialogs/LoadTmDialog.jsx b/src/components/Dialogs/LoadTmDialog.jsx index 9146825..bf433c5 100644 --- a/src/components/Dialogs/LoadTmDialog.jsx +++ b/src/components/Dialogs/LoadTmDialog.jsx @@ -34,6 +34,17 @@ export const LoadTmDialog = forwardRef((props, ref) => { const [choosenModel, setChoosenModel] = React.useState( [] ); + + const [showUrlForm, setShowUrlForm] = + React.useState(false); + + const show = () => { + setShowUrlForm(!showUrlForm); + }; + const [emporioUrl, setEmporioUrl] = React.useState( + `${process.env.REACT_APP_TM_SERVER_SCHEME}://${process.env.REACT_APP_TM_SERVER_HOST}:${process.env.REACT_APP_TM_SERVER_PORT}` + ); + const [pagination, setPagination] = React.useState({ currentPage: 0, loading: null, @@ -70,13 +81,16 @@ export const LoadTmDialog = forwardRef((props, ref) => { ); }; - const fetchThingModels = async ( + const fetchThingModels = async ({ page = 0, attribute = false, - searchText - ) => { + searchText, + remoteUrl = emporioUrl, + } = {}) => { + console.log(page); + console.log(remoteUrl); const offset = pagination.thingModelsPerPage * page; - let url = `${process.env.REACT_APP_TM_SERVER_SCHEME}://${process.env.REACT_APP_TM_SERVER_HOST}:${process.env.REACT_APP_TM_SERVER_PORT}/models?limit=${pagination.thingModelsPerPage}&offset=${offset}`; + let url = `${remoteUrl}/models?limit=${pagination.thingModelsPerPage}&offset=${offset}`; if (attribute) url += `&${attribute}=${searchText}`; const res = await fetch(url); const data = await res.json(); @@ -99,12 +113,12 @@ export const LoadTmDialog = forwardRef((props, ref) => { if (page < 0) return; const thingModels = searchText === "" - ? await fetchThingModels(page) - : await fetchThingModels( - page, - attribute, - searchText - ); + ? await fetchThingModels({ page: page }) + : await fetchThingModels({ + page: page, + attribute: attribute, + searchText: searchText, + }); if (thingModels.length <= 0) return; const thingModelObjects = thingModelToThingModelObjects(thingModels); @@ -112,6 +126,18 @@ export const LoadTmDialog = forwardRef((props, ref) => { setPagination({ ...pagination, currentPage: page }); }; + const changeThingModelUrl = async () => { + const url = document.getElementById("remote-url").value; + console.log(`URL: ${url}`); + //validate URL + setEmporioUrl(url); + const thingModels = await fetchThingModels({ + remoteUrl: url, + }); + return setThingModelObjects( + thingModelToThingModelObjects(thingModels) + ); + }; const searchThingModels = async (page = 0) => { const searchText = document.getElementById("search-id").value; @@ -121,12 +147,12 @@ export const LoadTmDialog = forwardRef((props, ref) => { const thingModels = searchText === "" - ? await fetchThingModels(page) - : await fetchThingModels( - page, - attribute, - searchText - ); + ? await fetchThingModels({ page: page }) + : await fetchThingModels({ + page: page, + attribute: attribute, + searchText: searchText, + }); return setThingModelObjects( thingModelToThingModelObjects(thingModels) ); @@ -138,7 +164,11 @@ export const LoadTmDialog = forwardRef((props, ref) => { setChoosenModel, setSelectedThingModel, searchThingModels, - paginate + paginate, + show, + showUrlForm, + emporioUrl, + changeThingModelUrl ); if (display) { @@ -177,10 +207,50 @@ const buildForm = ( setChoosenModel, setSelectedThingModel, searchThingModels, - paginate + paginate, + show, + showUrlForm, + emporioUrl, + changeUrl ) => { return ( <> + + + {showUrlForm && ( +
+ + + +
+ )} +
+
+ - -
- )} + {showUrlForm && ( +
+ + +
+ )} +
Date: Wed, 29 Jun 2022 15:25:47 +0200 Subject: [PATCH 12/28] feat: added advances options instead of tm repository, rework the design of TM repository input --- src/components/Dialogs/LoadTmDialog.jsx | 84 +++++++++++++------------ 1 file changed, 43 insertions(+), 41 deletions(-) diff --git a/src/components/Dialogs/LoadTmDialog.jsx b/src/components/Dialogs/LoadTmDialog.jsx index b8f2853..90e2c28 100644 --- a/src/components/Dialogs/LoadTmDialog.jsx +++ b/src/components/Dialogs/LoadTmDialog.jsx @@ -220,47 +220,6 @@ const buildForm = ( ) => { return ( <> -
- - - {showUrlForm && ( -
- - - -
- )} -
+ +
+
+ )} {thingModelObjects.map((thingModelObject, index) => ( Date: Wed, 29 Jun 2022 17:47:08 +0200 Subject: [PATCH 13/28] feat: change advance options from button to text --- src/components/Dialogs/LoadTmDialog.jsx | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/components/Dialogs/LoadTmDialog.jsx b/src/components/Dialogs/LoadTmDialog.jsx index 90e2c28..9df4656 100644 --- a/src/components/Dialogs/LoadTmDialog.jsx +++ b/src/components/Dialogs/LoadTmDialog.jsx @@ -253,11 +253,11 @@ const buildForm = ( Search
- +

{showUrlForm && (
@@ -284,15 +284,14 @@ const buildForm = ( defaultValue={emporioUrl} type="url" /> - +

)} From ea621b610bd0e59eaf4575bb2e6c25d8b669a84f Mon Sep 17 00:00:00 2001 From: ivan Date: Wed, 29 Jun 2022 17:58:00 +0200 Subject: [PATCH 14/28] fix: rollback change url to button --- src/components/Dialogs/LoadTmDialog.jsx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/components/Dialogs/LoadTmDialog.jsx b/src/components/Dialogs/LoadTmDialog.jsx index 9df4656..f5c9a0d 100644 --- a/src/components/Dialogs/LoadTmDialog.jsx +++ b/src/components/Dialogs/LoadTmDialog.jsx @@ -284,14 +284,15 @@ const buildForm = ( defaultValue={emporioUrl} type="url" /> -

{ changeUrl(); }} > Change -

+
)} From 0ee4dd50f69ccd6629c65fb5136fe321218f66f7 Mon Sep 17 00:00:00 2001 From: ivan Date: Tue, 5 Jul 2022 19:37:06 +0200 Subject: [PATCH 15/28] refactor: change multiple state variables to a single useReducer --- src/components/Dialogs/LoadTmDialog.jsx | 190 ++++++++++++++++-------- 1 file changed, 131 insertions(+), 59 deletions(-) diff --git a/src/components/Dialogs/LoadTmDialog.jsx b/src/components/Dialogs/LoadTmDialog.jsx index f5c9a0d..e88ba0b 100644 --- a/src/components/Dialogs/LoadTmDialog.jsx +++ b/src/components/Dialogs/LoadTmDialog.jsx @@ -14,6 +14,7 @@ import React, { forwardRef, useImperativeHandle, useContext, + useReducer, } from "react"; import ReactDOM from "react-dom"; import { DialogTemplate } from "./DialogTemplate"; @@ -24,33 +25,106 @@ import { ChevronLeft, } from "react-feather"; + +function tmReducer(state, action) { + switch (action.type) { + case "thingModels": { + return { + ...state, + thingModels: action.payload.map((thingModel) => { + return { thingModel: thingModel, select: false }; + }), + }; + } + case "selected": { + const index = action.payload; + let choosenModel; + const thingModels = state.thingModels.map( + (thingObject, thingIndex) => { + if (thingIndex === index) { + thingObject.selected = true; + choosenModel = thingObject.thingModel; + } else thingObject.selected = false; + return thingObject; + } + ); + return { + ...state, + thingModels: thingModels, + choosenModel: choosenModel, + }; + } + case "changePage": { + const pagination = { + ...state.pagination, + currentPage: action.payload, + }; + return { + ...state, + pagination: pagination, + showUrlForm: false, + }; + } + case "reset": { + const pagination = { + ...state.pagination, + currentPage: 0, + }; + return { + ...state, + pagination: pagination, + showUrlForm: false, + }; + } + case "field": { + return { + ...state, + [action.fieldName]: action.payload, + }; + } + default: + throw Error( + "Unexected reducer case in Thing Model Modal" + ); + } +} + +const initialState = { + thingModels: [], + choosenModel: null, + showUrlForm: false, + emporioUrl: `${process.env.REACT_APP_TM_SERVER_SCHEME}://${process.env.REACT_APP_TM_SERVER_HOST}:${process.env.REACT_APP_TM_SERVER_PORT}`, + pagination: { + currentPage: 0, + thingModelsPerPage: 5, + }, +}; + export const LoadTmDialog = forwardRef((props, ref) => { const context = useContext(ediTDorContext); const [display, setDisplay] = React.useState(() => { return false; }); - const [thingModelObjects, setThingModelObjects] = - React.useState([]); - const [choosenModel, setChoosenModel] = React.useState( - [] + const [state, dispatch] = useReducer( + tmReducer, + initialState ); - const [showUrlForm, setShowUrlForm] = - React.useState(false); + const { + thingModels, + choosenModel, + showUrlForm, + emporioUrl, + pagination, + } = state; const show = () => { - setShowUrlForm(!showUrlForm); + dispatch({ + type: "field", + fieldName: "showUrlForm", + payload: !showUrlForm, + }); }; - const [emporioUrl, setEmporioUrl] = React.useState( - `${process.env.REACT_APP_TM_SERVER_SCHEME}://${process.env.REACT_APP_TM_SERVER_HOST}:${process.env.REACT_APP_TM_SERVER_PORT}` - ); - - const [pagination, setPagination] = React.useState({ - currentPage: 0, - loading: null, - thingModelsPerPage: 5, - }); - useImperativeHandle(ref, () => { return { openModal: () => open(), @@ -59,26 +133,20 @@ export const LoadTmDialog = forwardRef((props, ref) => { }); const open = async () => { - const thingModels = await fetchThingModels(); - const thingModelObjects = - thingModelToThingModelObjects(thingModels); - setThingModelObjects(thingModelObjects); - setPagination({ ...pagination, loading: false }); + dispatch({ + type: "thingModels", + payload: await fetchThingModels(), + }); + dispatch({ type: "reset", payload: 0 }); setDisplay(true); }; const close = () => { setDisplay(false); }; + const setSelectedThingModel = (index) => { - setThingModelObjects( - thingModelObjects.map((thingObject, thingIndex) => { - if (thingIndex === index) - thingObject.selected = true; - else thingObject.selected = false; - return thingObject; - }) - ); + dispatch({ type: "selected", payload: index }); }; const fetchThingModels = async ({ @@ -87,8 +155,6 @@ export const LoadTmDialog = forwardRef((props, ref) => { searchText, remoteUrl = emporioUrl, } = {}) => { - console.log(page); - console.log(remoteUrl); const offset = pagination.thingModelsPerPage * page; let url = `${remoteUrl}/models?limit=${pagination.thingModelsPerPage}&offset=${offset}`; if (attribute) url += `&${attribute}=${searchText}`; @@ -97,20 +163,18 @@ export const LoadTmDialog = forwardRef((props, ref) => { return data; }; - const thingModelToThingModelObjects = (thingModels) => - thingModels.map((thingModel) => { - return { thingModel: thingModel, select: false }; - }); const paginate = async (direction) => { const page = direction === "right" ? pagination.currentPage + 1 : pagination.currentPage - 1; + if (page < 0) return; + const searchText = document.getElementById("search-id").value; const attribute = document.getElementById("search-option").value; - if (page < 0) return; + const thingModels = searchText === "" ? await fetchThingModels({ page: page }) @@ -120,10 +184,12 @@ export const LoadTmDialog = forwardRef((props, ref) => { searchText: searchText, }); if (thingModels.length <= 0) return; - const thingModelObjects = - thingModelToThingModelObjects(thingModels); - setThingModelObjects(thingModelObjects); - setPagination({ ...pagination, currentPage: page }); + + dispatch({ + type: "thingModels", + payload: thingModels, + }); + dispatch({ type: "changePage", payload: page }); }; const changeThingModelUrl = async () => { @@ -132,41 +198,47 @@ export const LoadTmDialog = forwardRef((props, ref) => { const thingModels = await fetchThingModels({ remoteUrl: url, }); - setEmporioUrl(url); + dispatch({ + type: "field", + fieldName: "emporioUrl", + payload: url, + }); - return setThingModelObjects( - thingModelToThingModelObjects(thingModels) - ); + return dispatch({ + type: "thingModels", + payload: thingModels, + }); } catch (error) { const msg = `Error processing URL - Thing Model Repository was not found`; alert(msg); } - //validate URL }; - const searchThingModels = async (page = 0) => { + const searchThingModels = async () => { const searchText = document.getElementById("search-id").value; const attribute = document.getElementById("search-option").value; - setPagination({ ...pagination, currentPage: 0 }); + + //TODO: is that right? + dispatch({ type: "reset" }); const thingModels = searchText === "" - ? await fetchThingModels({ page: page }) + ? await fetchThingModels() : await fetchThingModels({ - page: page, + page: 0, attribute: attribute, searchText: searchText, }); - return setThingModelObjects( - thingModelToThingModelObjects(thingModels) - ); + return dispatch({ + type: "thingModels", + payload: thingModels, + }); }; const content = buildForm( - thingModelObjects, + thingModels, pagination.currentPage, - setChoosenModel, setSelectedThingModel, searchThingModels, paginate, @@ -209,7 +281,7 @@ export const LoadTmDialog = forwardRef((props, ref) => { const buildForm = ( thingModelObjects, page, - setChoosenModel, + //setChoosenModel, setSelectedThingModel, searchThingModels, paginate, @@ -301,7 +373,7 @@ const buildForm = ( key={index} index={index} thingModelObject={thingModelObject} - setChoosenModel={setChoosenModel} + // setChoosenModel={setChoosenModel} setSelectedThingModel={setSelectedThingModel} /> ))} @@ -331,7 +403,7 @@ const buildForm = ( const ThingModel = ({ thingModelObject, index, - setChoosenModel, + //setChoosenModel, setSelectedThingModel, }) => { const types = formatThingModeltypes( @@ -353,7 +425,7 @@ const ThingModel = ({ }`} onClick={() => { setSelectedThingModel(index); - setChoosenModel(thingModelObject.thingModel); + //setChoosenModel(thingModelObject.thingModel); }} >
From fa473d6088da7082fb2215117891303604cf8e6e Mon Sep 17 00:00:00 2001 From: ivan Date: Tue, 5 Jul 2022 19:37:41 +0200 Subject: [PATCH 16/28] feat: add no border --- src/assets/main.css | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/assets/main.css b/src/assets/main.css index 7fd0ce5..859efb0 100644 --- a/src/assets/main.css +++ b/src/assets/main.css @@ -665,6 +665,11 @@ video { var(--tw-border-opacity) ); } +.border-none{ + border-style: none; +} + + .focus\:border-blue-500:focus { --tw-border-opacity: 1; From 90c7a3d20fa96769b75ab1f20e742e015f39cdbd Mon Sep 17 00:00:00 2001 From: ivan Date: Tue, 5 Jul 2022 19:45:45 +0200 Subject: [PATCH 17/28] fix: unable to submit 'Load TM' without selecting a thing model --- src/components/Dialogs/LoadTmDialog.jsx | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/components/Dialogs/LoadTmDialog.jsx b/src/components/Dialogs/LoadTmDialog.jsx index e88ba0b..ed53f2c 100644 --- a/src/components/Dialogs/LoadTmDialog.jsx +++ b/src/components/Dialogs/LoadTmDialog.jsx @@ -25,7 +25,6 @@ import { ChevronLeft, } from "react-feather"; - function tmReducer(state, action) { switch (action.type) { case "thingModels": { @@ -253,6 +252,7 @@ export const LoadTmDialog = forwardRef((props, ref) => { { + if (choosenModel === null) return; let linkedModel = {}; linkedModel[choosenModel["title"]] = choosenModel; context.updateLinkedTd(undefined); @@ -281,7 +281,6 @@ export const LoadTmDialog = forwardRef((props, ref) => { const buildForm = ( thingModelObjects, page, - //setChoosenModel, setSelectedThingModel, searchThingModels, paginate, @@ -373,7 +372,6 @@ const buildForm = ( key={index} index={index} thingModelObject={thingModelObject} - // setChoosenModel={setChoosenModel} setSelectedThingModel={setSelectedThingModel} /> ))} @@ -403,7 +401,6 @@ const buildForm = ( const ThingModel = ({ thingModelObject, index, - //setChoosenModel, setSelectedThingModel, }) => { const types = formatThingModeltypes( @@ -425,7 +422,6 @@ const ThingModel = ({ }`} onClick={() => { setSelectedThingModel(index); - //setChoosenModel(thingModelObject.thingModel); }} >
From ebac73dbafe420ea62febd518381bc9a30e5f505 Mon Sep 17 00:00:00 2001 From: ivan Date: Tue, 5 Jul 2022 20:01:16 +0200 Subject: [PATCH 18/28] refactor: move pagination buttons to its own component --- src/components/Dialogs/LoadTmDialog.jsx | 55 +++++++++++++++---------- 1 file changed, 34 insertions(+), 21 deletions(-) diff --git a/src/components/Dialogs/LoadTmDialog.jsx b/src/components/Dialogs/LoadTmDialog.jsx index ed53f2c..d4b04f2 100644 --- a/src/components/Dialogs/LoadTmDialog.jsx +++ b/src/components/Dialogs/LoadTmDialog.jsx @@ -291,6 +291,7 @@ const buildForm = ( ) => { return ( <> + {/* SEARCH BAR */}
- - - - -
- -
-
- + + + + {thingModelObjects.map((thingModelObject, index) => ( + + ))} - +
+ ); +}; - {/* ADV OPTIONS */} +const AdvancedOptions = ({ + showUrlForm, + emporioUrl, + show, + changeUrl, +}) => { + return ( + <>
show()} @@ -370,20 +402,6 @@ const buildForm = (
)} - {/* ADV OPTIONS */} - - {thingModelObjects.map((thingModelObject, index) => ( - - ))} - - {/* PAGINATION */} - - {/* PAGINATION */} ); }; From 01ed563b67db75606236ccfb78e0493071b8a38a Mon Sep 17 00:00:00 2001 From: ivan Date: Mon, 11 Jul 2022 17:09:17 +0200 Subject: [PATCH 20/28] refactor: changes made are not a new version --- package-lock.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index c0bfae0..a7876e3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "editdor", - "version": "0.4.0", + "version": "0.3.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "editdor", - "version": "0.4.0", + "version": "0.3.0", "dependencies": { "@babel/core": "7.9.0", "@svgr/webpack": "4.3.3", From 38a25be3feb303951851528effb8b08b1699bfdd Mon Sep 17 00:00:00 2001 From: ivan Date: Mon, 11 Jul 2022 17:56:45 +0200 Subject: [PATCH 21/28] style: rollback autoformat changes --- src/assets/main.css | 153 +++++++++++++++----------------------------- 1 file changed, 52 insertions(+), 101 deletions(-) diff --git a/src/assets/main.css b/src/assets/main.css index 859efb0..450c513 100644 --- a/src/assets/main.css +++ b/src/assets/main.css @@ -54,11 +54,16 @@ Improve consistency of default fonts in all browsers. (https://github.com/sindre */ body { - font-family: system-ui, -apple-system, - /* Firefox supports this but not yet `system-ui` */ - "Segoe UI", - Roboto, Helvetica, Arial, sans-serif, - "Apple Color Emoji", "Segoe UI Emoji"; + font-family: + system-ui, + -apple-system, /* Firefox supports this but not yet `system-ui` */ + 'Segoe UI', + Roboto, + Helvetica, + Arial, + sans-serif, + 'Apple Color Emoji', + 'Segoe UI Emoji'; } /* @@ -107,8 +112,13 @@ code, kbd, samp, pre { - font-family: ui-monospace, SFMono-Regular, Consolas, - "Liberation Mono", Menlo, monospace; /* 1 */ + font-family: + ui-monospace, + SFMono-Regular, + Consolas, + 'Liberation Mono', + Menlo, + monospace; /* 1 */ font-size: 1em; /* 2 */ } @@ -182,8 +192,7 @@ Remove the inheritance of text transform in Edge and Firefox. */ button, -select { - /* 1 */ +select { /* 1 */ text-transform: none; } @@ -192,8 +201,8 @@ Correct the inability to style clickable types in iOS and Safari. */ button, -[type="button"], -[type="submit"] { +[type='button'], +[type='submit'] { -webkit-appearance: button; } @@ -235,7 +244,7 @@ Correct the cursor style of increment and decrement buttons in Safari. 2. Correct the outline style in Safari. */ -[type="search"] { +[type='search'] { -webkit-appearance: textfield; /* 1 */ outline-offset: -2px; /* 2 */ } @@ -326,11 +335,8 @@ ul { * to override it to ensure consistency even when using the default theme. */ -html { - font-family: ui-sans-serif, system-ui, -apple-system, - BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", - Arial, "Noto Sans", sans-serif, "Apple Color Emoji", - "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; /* 1 */ + html { + font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; /* 1 */ line-height: 1.5; /* 2 */ } @@ -468,8 +474,7 @@ pre, code, kbd, samp { - font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, - Consolas, "Liberation Mono", "Courier New", monospace; + font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; } /** @@ -508,34 +513,22 @@ video { .space-x-2 > :not([hidden]) ~ :not([hidden]) { --tw-space-x-reverse: 0; margin-right: calc(0.5rem * var(--tw-space-x-reverse)); - margin-left: calc( - 0.5rem * calc(1 - var(--tw-space-x-reverse)) - ); + margin-left: calc(0.5rem * calc(1 - var(--tw-space-x-reverse))); } .appearance-none { -webkit-appearance: none; - appearance: none; + appearance: none; } .bg-white { --tw-bg-opacity: 1; - background-color: rgba( - 255, - 255, - 255, - var(--tw-bg-opacity) - ); + background-color: rgba(255, 255, 255, var(--tw-bg-opacity)); } .bg-formBlue { --tw-bg-opacity: 1; - background-color: rgba( - 10, - 132, - 255, - var(--tw-bg-opacity) - ); + background-color: rgba(10, 132, 255, var(--tw-bg-opacity)); } .bg-formRed { @@ -550,32 +543,17 @@ video { .bg-formOrange { --tw-bg-opacity: 1; - background-color: rgba( - 255, - 159, - 10, - var(--tw-bg-opacity) - ); + background-color: rgba(255, 159, 10, var(--tw-bg-opacity)); } .bg-gray-300 { --tw-bg-opacity: 1; - background-color: rgba( - 160, - 160, - 160, - var(--tw-bg-opacity) - ); + background-color: rgba(160, 160, 160, var(--tw-bg-opacity)); } .bg-gray-400 { --tw-bg-opacity: 1; - background-color: rgba( - 202, - 202, - 202, - var(--tw-bg-opacity) - ); + background-color: rgba(202, 202, 202, var(--tw-bg-opacity)); } .bg-gray-500 { @@ -613,12 +591,7 @@ video { .border-formBlue { --tw-border-opacity: 1; - border-color: rgba( - 10, - 132, - 255, - var(--tw-border-opacity) - ); + border-color: rgba(10, 132, 255, var(--tw-border-opacity)); } .border-formRed { @@ -633,22 +606,12 @@ video { .border-formOrange { --tw-border-opacity: 1; - border-color: rgba( - 255, - 159, - 10, - var(--tw-border-opacity) - ); + border-color: rgba(255, 159, 10, var(--tw-border-opacity)); } .border-gray-300 { --tw-border-opacity: 1; - border-color: rgba( - 160, - 160, - 160, - var(--tw-border-opacity) - ); + border-color: rgba(160, 160, 160, var(--tw-border-opacity)); } .border-gray-600 { @@ -658,29 +621,23 @@ video { .border-red-400 { --tw-border-opacity: 1; - border-color: rgba( - 252, - 129, - 129, - var(--tw-border-opacity) - ); -} -.border-none{ - border-style: none; + border-color: rgba(252, 129, 129, var(--tw-border-opacity)); } - - -.focus\:border-blue-500:focus { +.border-blue-500 { --tw-border-opacity: 1; border-color: rgba(0, 90, 156, var(--tw-border-opacity)); } -.border-blue-500 { +.focus\:border-blue-500:focus { --tw-border-opacity: 1; border-color: rgba(0, 90, 156, var(--tw-border-opacity)); } +.border-none{ + border-style: none; +} + .rounded { border-radius: 0.25rem; } @@ -1081,21 +1038,17 @@ video { } .shadow-md { - --tw-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), - 0 2px 4px -1px rgba(0, 0, 0, 0.06); - box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), - var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); + --tw-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); } .shadow-xl { - --tw-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), - 0 10px 10px -5px rgba(0, 0, 0, 0.04); - box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), - var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); + --tw-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); } * { - --tw-ring-inset: var(--tw-empty, /*!*/ /*!*/); + --tw-ring-inset: var(--tw-empty,/*!*/ /*!*/); --tw-ring-offset-width: 0px; --tw-ring-offset-color: #fff; --tw-ring-color: rgba(0, 90, 156, 0.5); @@ -1224,8 +1177,7 @@ video { } @keyframes ping { - 75%, - 100% { + 75%, 100% { transform: scale(2); opacity: 0; } @@ -1233,20 +1185,19 @@ video { @keyframes pulse { 50% { - opacity: 0.5; + opacity: .5; } } @keyframes bounce { - 0%, - 100% { + 0%, 100% { transform: translateY(-25%); - animation-timing-function: cubic-bezier(0.8, 0, 1, 1); + animation-timing-function: cubic-bezier(0.8,0,1,1); } 50% { transform: none; - animation-timing-function: cubic-bezier(0, 0, 0.2, 1); + animation-timing-function: cubic-bezier(0,0,0.2,1); } } @@ -1267,4 +1218,4 @@ video { } @media (min-width: 1536px) { -} +} \ No newline at end of file From 4e5677e7776c746067e39b7837a8c0052be3c886 Mon Sep 17 00:00:00 2001 From: ivan Date: Wed, 20 Jul 2022 15:16:17 +0200 Subject: [PATCH 22/28] feat: save remote feature --- src/components/App/AppHeader/AppHeader.jsx | 10 +- .../Dialogs/SaveTmRemotelyDialog.jsx | 163 ++++++++++++++++++ 2 files changed, 172 insertions(+), 1 deletion(-) create mode 100644 src/components/Dialogs/SaveTmRemotelyDialog.jsx diff --git a/src/components/App/AppHeader/AppHeader.jsx b/src/components/App/AppHeader/AppHeader.jsx index 59b0951..465eb80 100644 --- a/src/components/App/AppHeader/AppHeader.jsx +++ b/src/components/App/AppHeader/AppHeader.jsx @@ -21,6 +21,7 @@ import { ShareDialog } from "../../Dialogs/ShareDialog"; import { ConvertTmDialog } from "../../Dialogs/ConvertTmDialog"; import { CreateTdDialog } from "../../Dialogs/CreateTdDialog"; import { LoadTmDialog } from "../../Dialogs/LoadTmDialog"; +import { SaveTmRemotelyDialog } from "../../Dialogs/SaveTmRemotelyDialog"; import { getFileHandle, getFileHTML5, _readFileHTML5 } from "../../../util.js"; @@ -310,7 +311,12 @@ export default function AppHeader() { const loadTmDialog = React.useRef(); const openLoadTmDialog = () => { loadTmDialog.current.openModal() } + const saveTmRemotelyDialog = React.useRef(); + const openSaveTmRemotelyDialog = () => { saveTmRemotelyDialog.current.openModal() } + const useRemoteServer = process.env.REACT_APP_REMOTE_SERVER.toLocaleLowerCase() === "true"; + console.log(useRemoteServer); + console.log(useRemoteServer? 'true':'false'); return ( <>
@@ -325,9 +331,10 @@ export default function AppHeader() { - + {useRemoteServer && } {(hasNativeFS()) && } + {(context.isThingModel && useRemoteServer) && } {(context.showConvertBtn || context.isThingModel) && }
@@ -335,6 +342,7 @@ export default function AppHeader() { + { + const context = useContext(ediTDorContext); + const [display, setDisplay] = React.useState(() => { + return false; + }); + + useEffect(() => { + if (display === true) { + } + }, [display, context]); + + useImperativeHandle(ref, () => { + return { + openModal: () => open(), + close: () => close(), + }; + }); + + const open = () => { + setDisplay(true); + }; + + const close = () => { + setDisplay(false); + }; + + const checkForDuplicates = async ( + thingModel, + credential + ) => { + const response = await performPostRequest( + thingModel, + credential, + "models/is-duplicate" + ); + if (!response.ok) return handleError(response); + + const isDuplicateString = await response.text(); + return isDuplicateString.toLowerCase() === "true"; + }; + + const saveTm = async (thingModel, credential) => { + const isDuplicate = await checkForDuplicates(thingModel, credential); + let confirmation = false; + if (isDuplicate) { + const msg = + "The Thing Model send already exist in the give repository. Do you want to save it regardless?"; + confirmation = window.confirm(msg); + } + if (!isDuplicate || confirmation) { + const response = await performPostRequest(thingModel, credential); + if (!response.ok) handleError(response); + } + }; + + const handleError = (response) => { + let msg; + switch (response.status) { + case 401: + msg = "Invalid credentials provided"; + break; + case 500: + msg = + "Thing model repository is having troubles processing your request"; + break; + case 400: + msg = "Invalid thing model provided"; + break; + default: + msg = `We ran into an error trying to save your TD.`; + } + return alert(msg); + }; + + const performPostRequest = async ( + thingModel, + credential, + path = "models" + ) => { + return await fetch( + `${context.tmRepositoryUrl}/${path}`, + { + method: "POST", + body: thingModel, + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${credential}`, + }, + } + ); + }; + + const urlField = createCredentialField(); + + if (display) { + return ReactDOM.createPortal( + { + const credential = document.getElementById( + "credential-field" + ).value; + if (credential === null) return; + const thingModel = context.offlineTD; + saveTm(thingModel, credential); + close(); + }} + children={urlField} + title={"Create new TM"} + description={ + "Create a new TM in the remote Thing Model Repository" + } + />, + document.getElementById("modal-root") + ); + } + + return null; + } +); + +const createCredentialField = () => { + return ( +
+ + +
+ ); +}; From 4c1a0a3ef3295872ba66f458cb61b27618f08558 Mon Sep 17 00:00:00 2001 From: ivan Date: Wed, 20 Jul 2022 15:16:54 +0200 Subject: [PATCH 23/28] fix: update isThingModel context property --- src/components/Dialogs/CreateTdDialog.jsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/Dialogs/CreateTdDialog.jsx b/src/components/Dialogs/CreateTdDialog.jsx index 4ce63fc..81e5f54 100644 --- a/src/components/Dialogs/CreateTdDialog.jsx +++ b/src/components/Dialogs/CreateTdDialog.jsx @@ -57,6 +57,7 @@ export const CreateTdDialog = forwardRef((props, ref) => { context.updateLinkedTd(undefined) context.addLinkedTd(linkedTd) context.updateShowConvertBtn(type === "TM"); + context.updateIsThingModel(type === "TM") context.updateOfflineTD(JSON.stringify(td, null, "\t"),"AppHeader"); close(); }} From b9d52590bfb7319bb006d84e22f6d6bbb55857c1 Mon Sep 17 00:00:00 2001 From: ivan Date: Wed, 20 Jul 2022 15:17:44 +0200 Subject: [PATCH 24/28] fix: is misleading to have duplicate context definition. Modifying this file did not modify the actual context variables --- src/context/ediTDorContext.js | 32 +++++--------------------------- 1 file changed, 5 insertions(+), 27 deletions(-) diff --git a/src/context/ediTDorContext.js b/src/context/ediTDorContext.js index 17e4745..147b0c2 100644 --- a/src/context/ediTDorContext.js +++ b/src/context/ediTDorContext.js @@ -1,37 +1,15 @@ /******************************************************************************** * Copyright (c) 2018 - 2020 Contributors to the Eclipse Foundation - * + * * See the NOTICE file(s) distributed with this work for additional * information regarding copyright ownership. - * + * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0, or the W3C Software Notice and - * + * * SPDX-License-Identifier: EPL-2.0 OR W3C-20150513 ********************************************************************************/ -import React from 'react'; +import React from "react"; -export default React.createContext({ - offlineTD: '', - theme: 'dark', - isModified: false, - isThingModel: undefined, - name: '', - fileHandle:'', - showConvertBtn: false, - linkedTd:{}, - updateOfflineTD: td => {}, - updateIsModified: isModified => {}, - updateIsThingModel: isThingModel => {}, - setFileHandle: handle => {}, - removeForm: form => {}, - addForm: form => {}, - removeLink: link => {}, - addActionForm: params => {}, - addEventForm: params => {}, - removeOneOfAKindReducer: (kind, oneOfAKind) => {}, - updateShowConvertBtn: showConvertBtn => {}, - addLinkedTd: linkedTd => {}, - updateLinkedTd: linkedTd => {} -}); \ No newline at end of file +export default React.createContext({}); \ No newline at end of file From 705d56f9cc68f227ae2540336c2a934d2a56c10f Mon Sep 17 00:00:00 2001 From: ivan Date: Wed, 20 Jul 2022 15:18:13 +0200 Subject: [PATCH 25/28] feat: add emporio url as a context property --- src/components/Dialogs/LoadTmDialog.jsx | 16 ++++++---------- src/context/GlobalState.js | 15 ++++++++++++--- src/context/editorReducers.js | 8 ++++++++ 3 files changed, 26 insertions(+), 13 deletions(-) diff --git a/src/components/Dialogs/LoadTmDialog.jsx b/src/components/Dialogs/LoadTmDialog.jsx index e0087a8..dc0da76 100644 --- a/src/components/Dialogs/LoadTmDialog.jsx +++ b/src/components/Dialogs/LoadTmDialog.jsx @@ -92,7 +92,6 @@ const initialState = { thingModels: [], choosenModel: null, showUrlForm: false, - emporioUrl: `${process.env.REACT_APP_TM_SERVER_SCHEME}://${process.env.REACT_APP_TM_SERVER_HOST}:${process.env.REACT_APP_TM_SERVER_PORT}`, pagination: { currentPage: 0, thingModelsPerPage: 5, @@ -101,6 +100,7 @@ const initialState = { export const LoadTmDialog = forwardRef((props, ref) => { const context = useContext(ediTDorContext); + const [display, setDisplay] = React.useState(() => { return false; }); @@ -113,7 +113,6 @@ export const LoadTmDialog = forwardRef((props, ref) => { thingModels, choosenModel, showUrlForm, - emporioUrl, pagination, } = state; @@ -152,7 +151,7 @@ export const LoadTmDialog = forwardRef((props, ref) => { page = 0, attribute = false, searchText, - remoteUrl = emporioUrl, + remoteUrl = context.tmRepositoryUrl, } = {}) => { const offset = pagination.thingModelsPerPage * page; let url = `${remoteUrl}/models?limit=${pagination.thingModelsPerPage}&offset=${offset}`; @@ -197,11 +196,7 @@ export const LoadTmDialog = forwardRef((props, ref) => { const thingModels = await fetchThingModels({ remoteUrl: url, }); - dispatch({ - type: "field", - fieldName: "emporioUrl", - payload: url, - }); + context.updateTmRepositoryUrl(url); return dispatch({ type: "thingModels", @@ -243,7 +238,7 @@ export const LoadTmDialog = forwardRef((props, ref) => { paginate, show, showUrlForm, - emporioUrl, + context.tmRepositoryUrl, changeThingModelUrl ); @@ -258,6 +253,7 @@ export const LoadTmDialog = forwardRef((props, ref) => { context.updateLinkedTd(undefined); context.addLinkedTd(linkedModel); context.updateShowConvertBtn(true); + context.updateIsThingModel(true); context.updateOfflineTD( JSON.stringify(choosenModel, null, "\t"), "AppHeader" @@ -292,7 +288,7 @@ const buildForm = ( return ( <> - + { - const [editdorState, dispatch] = useReducer(editdorReducer, { offlineTD: '', theme: 'dark' }); + const [editdorState, dispatch] = useReducer(editdorReducer, { + offlineTD: '', + theme: 'dark', + tmRepositoryUrl: `${process.env.REACT_APP_TM_SERVER_SCHEME}://${process.env.REACT_APP_TM_SERVER_HOST}:${process.env.REACT_APP_TM_SERVER_PORT}` + }); const updateOfflineTD = (offlineTD, props) => { dispatch({ type: UPDATE_OFFLINE_TD, offlineTD: offlineTD }); @@ -59,6 +63,9 @@ const GlobalState = props => { const updateShowConvertBtn = showConvertBtn => { dispatch({ type: UPDATE_SHOW_CONVERT_BTN, showConvertBtn: showConvertBtn }); }; + const updateTmRepositoryUrl = tmRepositoryUrl => { + dispatch({ type: UPDATE_TM_REPOSITORY_URL, tmRepositoryUrl: tmRepositoryUrl }); + }; const addLinkedTd = linkedTd => { dispatch({ type: ADD_LINKED_TD, linkedTd: linkedTd }); @@ -79,6 +86,7 @@ const GlobalState = props => { fileHandle: editdorState.fileHandle, showConvertBtn: editdorState.showConvertBtn, linkedTd: editdorState.linkedTd, + tmRepositoryUrl: editdorState.tmRepositoryUrl, updateOfflineTD, updateIsModified, updateIsThingModel, @@ -91,7 +99,8 @@ const GlobalState = props => { removeOneOfAKindReducer, updateShowConvertBtn, addLinkedTd, - updateLinkedTd + updateLinkedTd, + updateTmRepositoryUrl, }} > {props.children} diff --git a/src/context/editorReducers.js b/src/context/editorReducers.js index 029aefa..541e3ba 100644 --- a/src/context/editorReducers.js +++ b/src/context/editorReducers.js @@ -21,6 +21,7 @@ export const ADD_ACTIONFORM_TO_TD = 'ADD_ACTIONFORM_TO_TD'; export const ADD_EVENTFORM_TO_TD = 'ADD_EVENTFORM_TO_TD'; export const REMOVE_ONE_OF_A_KIND_FROM_TD = 'REMOVE_ONE_OF_A_KIND_FROM_TD'; export const UPDATE_SHOW_CONVERT_BTN = 'UPDATE_SHOW_CONVERT_BTN'; +export const UPDATE_TM_REPOSITORY_URL = 'UPDATE_TM_REPOSITORY_URL'; export const ADD_LINKED_TD = 'ADD_LINKED_TD'; export const UPDATE_LINKED_TD = 'UPDATE_LINKED_TD' @@ -188,6 +189,11 @@ const updateShowConvertBtn = (showConvertBtn, state) => { return { ...state, showConvertBtn: showConvertBtn }; }; +const updateTmRepositoryUrl = (tmRepositoryUrl, state) => { + console.log('updateTmRepositoryUrl') + return { ...state, tmRepositoryUrl: tmRepositoryUrl }; +}; + const editdorReducer = (state, action) => { switch (action.type) { case UPDATE_OFFLINE_TD: @@ -213,6 +219,8 @@ const editdorReducer = (state, action) => { return addEventFormReducer(action.params, state) case UPDATE_SHOW_CONVERT_BTN: return updateShowConvertBtn(action.showConvertBtn, state); + case UPDATE_TM_REPOSITORY_URL: + return updateTmRepositoryUrl(action.tmRepositoryUrl, state); case ADD_LINKED_TD: return addLinkedTd(action.linkedTd,state) case UPDATE_LINKED_TD: From 3c27e96152f01aabfe5433818a5be4e90cf52047 Mon Sep 17 00:00:00 2001 From: ivan Date: Wed, 20 Jul 2022 15:18:44 +0200 Subject: [PATCH 26/28] feat: add an enviromental variable to control if a remote TM repository is going to be use or not --- .env | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.env b/.env index 74c58bb..ede85ab 100644 --- a/.env +++ b/.env @@ -1,3 +1,4 @@ REACT_APP_TM_SERVER_SCHEME=https REACT_APP_TM_SERVER_HOST=api.emporio.vaimee.it -REACT_APP_TM_SERVER_PORT=80 \ No newline at end of file +REACT_APP_TM_SERVER_PORT=80 +REACT_APP_REMOTE_SERVER=false \ No newline at end of file From 0377ecd3f8ae5c4479f0f6a42d516afc4fbf6094 Mon Sep 17 00:00:00 2001 From: ivan Date: Wed, 20 Jul 2022 20:50:27 +0200 Subject: [PATCH 27/28] refactor: move AdvancedOptions componet to its own file --- src/components/Dialogs/LoadTmDialog.jsx | 75 +------------------ .../Dialogs/components/AdvancedOptions.jsx | 57 ++++++++++++++ 2 files changed, 58 insertions(+), 74 deletions(-) create mode 100644 src/components/Dialogs/components/AdvancedOptions.jsx diff --git a/src/components/Dialogs/LoadTmDialog.jsx b/src/components/Dialogs/LoadTmDialog.jsx index dc0da76..9559f82 100644 --- a/src/components/Dialogs/LoadTmDialog.jsx +++ b/src/components/Dialogs/LoadTmDialog.jsx @@ -18,6 +18,7 @@ import React, { } from "react"; import ReactDOM from "react-dom"; import { DialogTemplate } from "./DialogTemplate"; +import { AdvancedOptions } from "./components/AdvancedOptions" import ediTDorContext from "../../context/ediTDorContext"; import { ChevronDown, @@ -61,7 +62,6 @@ function tmReducer(state, action) { return { ...state, pagination: pagination, - showUrlForm: false, }; } case "reset": { @@ -72,7 +72,6 @@ function tmReducer(state, action) { return { ...state, pagination: pagination, - showUrlForm: false, }; } case "field": { @@ -91,7 +90,6 @@ function tmReducer(state, action) { const initialState = { thingModels: [], choosenModel: null, - showUrlForm: false, pagination: { currentPage: 0, thingModelsPerPage: 5, @@ -112,17 +110,9 @@ export const LoadTmDialog = forwardRef((props, ref) => { const { thingModels, choosenModel, - showUrlForm, pagination, } = state; - const show = () => { - dispatch({ - type: "field", - fieldName: "showUrlForm", - payload: !showUrlForm, - }); - }; useImperativeHandle(ref, () => { return { openModal: () => open(), @@ -236,9 +226,6 @@ export const LoadTmDialog = forwardRef((props, ref) => { setSelectedThingModel, searchThingModels, paginate, - show, - showUrlForm, - context.tmRepositoryUrl, changeThingModelUrl ); @@ -280,9 +267,6 @@ const buildForm = ( setSelectedThingModel, searchThingModels, paginate, - show, - showUrlForm, - emporioUrl, changeUrl ) => { return ( @@ -290,9 +274,6 @@ const buildForm = ( @@ -348,60 +329,6 @@ const SearchBar = ({ searchThingModels }) => { ); }; -const AdvancedOptions = ({ - showUrlForm, - emporioUrl, - show, - changeUrl, -}) => { - return ( - <> -
show()} - > -
-

- Advanced Options -

-
- {showUrlForm === true ? ( - - ) : ( - - )} -
-
-
- {showUrlForm && ( -
- -
- - -
-
- )} - - ); -}; - const Pagination = ({ paginate, page }) => { return (
diff --git a/src/components/Dialogs/components/AdvancedOptions.jsx b/src/components/Dialogs/components/AdvancedOptions.jsx new file mode 100644 index 0000000..35e38d3 --- /dev/null +++ b/src/components/Dialogs/components/AdvancedOptions.jsx @@ -0,0 +1,57 @@ +import { ChevronDown, ChevronRight } from "react-feather"; +import ediTDorContext from "../../../context/ediTDorContext"; +import React from "react"; + +export const AdvancedOptions = ({ changeUrl }) => { + const context = React.useContext(ediTDorContext); + const [showUrl, setShowUrl] = React.useState(false); + const show = () => { + setShowUrl(!showUrl); + }; + return ( + <> +
show()} + > +
+

+ Advanced Options +

+
+ {showUrl === true ? ( + + ) : ( + + )} +
+
+
+ {showUrl && ( +
+ +
+ + +
+
+ )} + + ); +}; From 6c80de40e1943687feecd388234f18dbcc906109 Mon Sep 17 00:00:00 2001 From: ivan Date: Wed, 20 Jul 2022 20:50:57 +0200 Subject: [PATCH 28/28] feat: add AdvancedOptions in SaveRemote Dialog --- .../Dialogs/SaveTmRemotelyDialog.jsx | 37 +++++++++++++++---- 1 file changed, 29 insertions(+), 8 deletions(-) diff --git a/src/components/Dialogs/SaveTmRemotelyDialog.jsx b/src/components/Dialogs/SaveTmRemotelyDialog.jsx index c7b5b2e..50fcdad 100644 --- a/src/components/Dialogs/SaveTmRemotelyDialog.jsx +++ b/src/components/Dialogs/SaveTmRemotelyDialog.jsx @@ -18,10 +18,10 @@ import React, { } from "react"; import ReactDOM from "react-dom"; import ediTDorContext from "../../context/ediTDorContext"; +import { AdvancedOptions } from "./components/AdvancedOptions" import { DialogTemplate } from "./DialogTemplate"; -export const SaveTmRemotelyDialog = forwardRef( - (props, ref) => { +export const SaveTmRemotelyDialog = forwardRef((props, ref) => { const context = useContext(ediTDorContext); const [display, setDisplay] = React.useState(() => { return false; @@ -63,7 +63,10 @@ export const SaveTmRemotelyDialog = forwardRef( }; const saveTm = async (thingModel, credential) => { - const isDuplicate = await checkForDuplicates(thingModel, credential); + const isDuplicate = await checkForDuplicates( + thingModel, + credential + ); let confirmation = false; if (isDuplicate) { const msg = @@ -71,7 +74,10 @@ export const SaveTmRemotelyDialog = forwardRef( confirmation = window.confirm(msg); } if (!isDuplicate || confirmation) { - const response = await performPostRequest(thingModel, credential); + const response = await performPostRequest( + thingModel, + credential + ); if (!response.ok) handleError(response); } }; @@ -94,7 +100,7 @@ export const SaveTmRemotelyDialog = forwardRef( } return alert(msg); }; - + const performPostRequest = async ( thingModel, credential, @@ -112,8 +118,20 @@ export const SaveTmRemotelyDialog = forwardRef( } ); }; + const changeUrl = async () =>{ + const url = document.getElementById("remote-url").value; + try { + //* is this the best way to check if this url is a valid thing model repository? + await fetch(`${url}/models?limit=1`) + context.updateTmRepositoryUrl(url); + } catch (error) { + const msg = `Error processing URL - Thing Model Repository was not found`; + alert(msg); + } - const urlField = createCredentialField(); + + } + const urlField = createCredentialField(changeUrl); if (display) { return ReactDOM.createPortal( @@ -143,14 +161,17 @@ export const SaveTmRemotelyDialog = forwardRef( } ); -const createCredentialField = () => { +const createCredentialField = (changeUrl) => { return (
+