Skip to content

Commit

Permalink
Add 'Replace' functionality to incoming logs using editable text for …
Browse files Browse the repository at this point in the history
…JSON leafs
  • Loading branch information
andreaj00 committed Feb 15, 2024
1 parent 1d36644 commit 6e0ce0a
Show file tree
Hide file tree
Showing 2 changed files with 210 additions and 8 deletions.
12 changes: 12 additions & 0 deletions frontend/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -80,4 +80,16 @@ button {
border-radius: 5px;
margin-top: 5px;
overflow: auto;
}

/* EditableText*/
/* Style for spans with text content */
.editable-text-span {
border: 1px solid #000;
}

/* Style for empty spans with padding */
.editable-text-span:empty {
padding-left: 10px;
padding-right: 10px;
}
206 changes: 198 additions & 8 deletions frontend/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,16 @@

<script data-plugins="transform-modules-umd" type="text/babel" data-presets="react" data-type="module">
// Import React and its hooks
const { useState, useEffect } = React;
const { useState, useEffect, useRef } = React;

// WebSocketConsole component
const WebSocketConsole = ({ port }) => {
const [ws, setWs] = useState(null);
const [incomingMessage, setIncomingMessage] = useState('');
const [editableText, setEditableText] = useState('');
const [errors, setErrors] = useState([]);
const [loggedMessages, setLoggedMessages] = useState([]);
let incomingMessageJSON= useRef({});

// After the element rendering, connect to the given websocket port
useEffect(() => {
Expand All @@ -50,6 +52,14 @@
};
}, []);

// When a new incomingMessage log is given, parse it to editable
useEffect(() => {
if (incomingMessage !== null && incomingMessage !== "" ){
incomingMessageJSON.current = JSON.parse(incomingMessage);
setEditableText(jsonToEditableText(incomingMessageJSON.current, null, '', 0));
}
}, [incomingMessage]);

function handleOpenWebSocket(){
const message = `WebSocket connection for Port ${port} established.`
setLoggedMessages(prevMessages => [...prevMessages, message]);
Expand All @@ -62,7 +72,7 @@
function handleMessageWebSocket(event){
const message = decomposeJSON(event.data);
console.log("currently in sync and waiting", port);
setIncomingMessage(message);
setIncomingMessage(message); // This will launch the useEffect dependent on IncomingMessage
}

function handleErrorWebSocket(event) {
Expand Down Expand Up @@ -91,15 +101,20 @@
return JSON.stringify(parsedMessage, null, 2);
}

const clearIncomingLogMessages = () => {
setIncomingMessage("");
setEditableText("");
incomingMessageJSON = {};
}

const acceptIncomingLog = () => {
if(incomingMessage === ""){
return;
}
console.log("new message accepted on port: ", port);

// Accept Log and clear the input
// Accept ORIGINAL Log (without modifications)
setLoggedMessages(prevMessages => [...prevMessages, incomingMessage]);
setIncomingMessage("");

// Send the response on the WebSocket
let webSocketResponse = {
Expand All @@ -108,23 +123,197 @@
};
const webSocketResponseJSON = JSON.stringify(webSocketResponse);
ws.send(webSocketResponseJSON);

// Clear the input
clearIncomingLogMessages();
};

const declineIncomingLog = () => {
const replaceIncomingLog = () => {
if(incomingMessage === ""){
return;
}

// Decline Log and clear the input
setIncomingMessage("");
// Accept REPLACED Log
const acceptingModifiedMessage = JSON.stringify(incomingMessageJSON.current, null, 2)
setLoggedMessages(prevMessages => [...prevMessages, acceptingModifiedMessage]);

// Send the response on the WebSocket
// Note, that we still don't need the stringify version for incomingMessageJSON.current as we still need
// an Object, which will be later on stringified
let webSocketResponse = {
"Type": "replace",
"Value": JSON.stringify(incomingMessageJSON.current)
};
const webSocketResponseJSON = JSON.stringify(webSocketResponse);
console.log("webSocketResponseJSON", webSocketResponseJSON, webSocketResponse)
ws.send(webSocketResponseJSON);

// Clear the input
clearIncomingLogMessages();
};

const declineIncomingLog = () => {
if(incomingMessage === ""){
return;
}

// Send the decline response on the WebSocket
let webSocketResponse = {
"Type": "decline",
"Value": ""
};
const webSocketResponseJSON = JSON.stringify(webSocketResponse);
ws.send(webSocketResponseJSON);

// Clear the input
clearIncomingLogMessages();
};

// Modifiable text component: When clicking on it, it becomes an input text
const EditableText = ({ text, textType, jsonReference, jsonReferenceKey, onUpdate }) => {
if(text === null || text === 'undefined'){
text = ""; // Set text to an empty string if it's null or undefined
}
const [isEditing, setIsEditing] = useState(false);
const [editedText, setEditedText] = useState(text);
const editedTextType = useRef(textType);
const textRef = useRef();
const myJsonReference = useRef(jsonReference);
const myJsonReferenceKey = useRef(jsonReferenceKey);
console.log("Initializing myJsonReference", myJsonReference, text, jsonReference);


const handleDoubleClick = () => {
setIsEditing(true);
};

const handleBlur = () => {
setIsEditing(false);

// Update parent with the new text
onUpdate(editedText, editedTextType.current, myJsonReference.current, myJsonReferenceKey.current);
};

const handleChange = (e) => {
setEditedText(e.target.value);
};

return (
<label onClick={handleDoubleClick}>
{isEditing ? (
<input
type="text"
value={editedText}
onChange={handleChange}
onBlur={handleBlur}
ref={textRef}
/>
) : (
<span className="editable-text-span">{editedText}</span>
)}
</label>
);
};

// Recursive function to parse the JSON and convert the leafs into EditableText
// Remember that the JSON should be passed by reference in JavaScript; therefore, we can modify the same object.
// Note that if the text is a boolean, it will be converted firstly to a string. After modification,
// if it is still an acceptable value for a boolean, it will be converted back.
// Note that if the text is a number, it will be converted firstly to a string. After modification,
// if it is still an acceptable value for a number, it will be converted back.
// Note that if the text is null, once the text is clicked, it will become an empty string.
const jsonToEditableText = (json, parentJson, parentKey, depth) => {
const space = ' '; // Two spaces for each level of depth
const indentation = space.repeat(depth);
if (typeof json === 'object' && json !== null) {
if (Array.isArray(json)) {
// Array object
return (
<div>
{indentation}&#91;
{
json.map((item, index) => {
return (
<div key={index}>
{indentation}{space}
{jsonToEditableText(item, json, index, depth + 1)}
</div>
);
})
}
{indentation}&#93;
</div>
);
} else if (Object.keys(json).length === 0) {
// Empty object
// Return inline {}
return (
<span> &#123;&#125;
</span>
);
}
else{
// Normal non-empty Object
return (
<div>
{indentation}&#123;
{
Object.keys(json).map((key) => {
return (
<div key={key}>
{indentation}
<strong>{space}"{key}":</strong> {jsonToEditableText(json[key], json, key, depth + 1)}
</div>
);
})
}
{indentation}&#125;
</div>
);
}
} else {
// Manage json type edge cases
// Skipped the edge case of given a number, as a number will be automatically converted to string
// Manage the case is given a boolean and convert it back to string to print
const textType = typeof json;
if (textType === 'boolean') {
json = json ? 'true' : 'false';
}
return (
<EditableText
text={json}
textType= {textType}
jsonReference={parentJson}
jsonReferenceKey = {parentKey}
onUpdate={(newText, textType, jsonReference, jsonReferenceKey) => {
// On screen print it as string but on the background save it with the actual type
json = newText; // String on screen
let mappingValue = newText;
if (textType === 'boolean') {
if (mappingValue === 'true') {
mappingValue = true
}
else if (mappingValue === 'false') {
mappingValue = false
}
else { // The boolean has been converted to a string
textType = 'string'
}
}
else if (textType === 'number') {
const temp = Number(mappingValue)
if (isNaN(temp)) { // Not a valid Number
textType = 'string'
}
else {
mappingValue = temp;
}
}
jsonReference[jsonReferenceKey] = mappingValue; // Actual value (NOT String)
}}
/>
);
}
};

return (
Expand All @@ -142,10 +331,11 @@
<div id="incoming-log-div" className="incoming-log-div">
Incoming Log:
<div id="incoming-log-placeholder" className="incoming-log-screen json-message">
<pre>{incomingMessage}</pre>
<pre>{editableText}</pre>
</div>
<div id="incoming-log-buttons" className="incoming-log-buttons">
<button onClick={acceptIncomingLog}>Accept</button>
<button onClick={replaceIncomingLog}>Replace</button>
<button onClick={declineIncomingLog}>Decline</button>
</div>
</div>
Expand Down

0 comments on commit 6e0ce0a

Please sign in to comment.