task locking

main
alex 2023-05-29 18:13:38 +02:00
parent 837e7ee23d
commit 079e7d526d
2 changed files with 91 additions and 30 deletions

View File

@ -1,6 +1,5 @@
import {
Alert,
Avatar,
Button,
Form,
Input,
@ -10,10 +9,9 @@ import {
Result,
Steps,
Tag,
Tooltip,
notification,
} from "antd";
import { useContext } from "react";
import { useContext, useRef, useState } from "react";
import { useNavigate, useParams } from "react-router-dom";
import {
Constants,
@ -23,12 +21,9 @@ import {
GetDuration,
MyAvatar,
getUserId,
setNativeValue,
} from "../../utils";
import {
AntDesignOutlined,
InfoCircleOutlined,
LockOutlined,
} from "@ant-design/icons";
import { InfoCircleOutlined, LockOutlined } from "@ant-design/icons";
export default function GroupTasksViewModal({ isOpen }) {
const webSocketContext = useContext(WebSocketContext);
@ -102,7 +97,6 @@ export default function GroupTasksViewModal({ isOpen }) {
case Constants.GROUP_TASKS_STATUS.RUNNING:
return "process";
case Constants.GROUP_TASKS_STATUS.CANCELED:
return "error";
case Constants.GROUP_TASKS_STATUS.FAILED:
return "error";
case Constants.GROUP_TASKS_STATUS.INPUT_REQUIRED:
@ -145,7 +139,10 @@ export default function GroupTasksViewModal({ isOpen }) {
}
taskInputs.push({
parameterName: specifiedTaskInputs[i].id,
parameterName:
specifiedTaskInputs[i].id.split(
"-"
)[6] /* Format: UUID-STEP-PARAMETER_NAME */,
value: specifiedTaskInputs[i].value,
});
}
@ -528,6 +525,8 @@ function InputRequiredHandler({
step,
taskLockedByUserId,
}) {
const [inputFields, setInputFields] = useState({});
const globalInputs = JSON.parse(currentGroupTask.GlobalInputs);
const getDefaultValue = (groupTaskParameter) => {
@ -552,18 +551,29 @@ function InputRequiredHandler({
);
};
let lastChanges = [];
let lastChanges = useRef([]);
let typingTimer = useRef();
const onInputChange = (currentGroupTaskId) => {
lastChanges = lastChanges.filter(
const onInputChange = (
inputValue,
currentGroupTaskId,
groupTaskParameterName
) => {
setInputFields((fields) => {
return ({ ...fields }[groupTaskParameterName] = inputValue);
});
if (taskLockedByUserId !== "") return;
lastChanges.current = lastChanges.current.filter(
(lc) =>
Date.now() - new Date(lc.changeTime) < Constants.GROUP_TASK_LOCKED_TIME
);
const lastChange = lastChanges.find((lC) => lC.step === step);
const lastChange = lastChanges.current.find((lC) => lC.step === step);
if (lastChange === undefined) {
lastChanges.push({
lastChanges.current.push({
step: step,
changeTime: Date.now(),
});
@ -574,6 +584,19 @@ function InputRequiredHandler({
step: step,
});
}
// sync input value with other web clients
clearTimeout(typingTimer.current);
typingTimer.current = setTimeout(() => {
webSocketContext.SendSocketMessage(
SentMessagesCommands.UpdateGroupTaskStepUserInputValue,
{
element: `${currentGroupTaskId}-${step}-${groupTaskParameterName}`,
value: inputValue,
}
);
}, 500);
};
return (
@ -592,15 +615,17 @@ function InputRequiredHandler({
>
<Input
key={"input-" + groupTaskParameter.parameterName}
id={groupTaskParameter.parameterName}
id={`${currentGroupTask.Id}-${step}-${groupTaskParameter.parameterName}`}
defaultValue={getDefaultValue(groupTaskParameter)}
disabled={taskLockedByUserId !== ""}
onChange={() =>
onChange={(e) =>
onInputChange(
e.target.value,
currentGroupTask.Id,
groupTaskParameter.parameterName
)
}
value={inputFields[groupTaskParameter.parameterName]}
/>
</Form.Item>
);
@ -616,12 +641,14 @@ function InputRequiredHandler({
>
<InputNumber
key={"fitem-" + groupTaskParameter.parameterName}
id={groupTaskParameter.parameterName}
id={`${currentGroupTask.Id}-${step}-${groupTaskParameter.parameterName}`}
style={{ width: "100%" }}
defaultValue={getDefaultValue(groupTaskParameter)}
disabled={taskLockedByUserId !== ""}
onChange={() =>
max={Number.MAX_SAFE_INTEGER}
onChange={(e) =>
onInputChange(
e,
currentGroupTask.Id,
groupTaskParameter.parameterName
)

View File

@ -1,6 +1,6 @@
import { UserOutlined } from "@ant-design/icons";
import { Avatar, Badge, Tooltip } from "antd";
import { createContext, useEffect, useRef, useState } from "react";
import { createContext, useContext, useEffect, useRef, useState } from "react";
import { useNavigate } from "react-router-dom";
import { Buffer } from "buffer";
@ -111,6 +111,7 @@ const ReceivedMessagesCommands = {
UpdateScannerLastUsed: 15,
TaskLocked: 16,
TaskUnlocked: 17,
UpdateGroupTaskStepUserInputValue: 18,
};
// commands sent to the backend server
@ -120,6 +121,7 @@ export const SentMessagesCommands = {
TaskContinueTaskStep: 3,
ReloadGroupTasks: 4,
TaskLocking: 5,
UpdateGroupTaskStepUserInputValue: 6,
};
export function WebSocketProvider({
@ -146,21 +148,19 @@ export function WebSocketProvider({
const navigate = useNavigate();
const StartGroupTasksOpenModalRememberIdRef = useRef(null);
let socket = null;
//let socket = null;
const ws = useRef(null);
const connect = () => {
socket = new WebSocket(Constants.WS_ADDRESS + "?auth=" + userSession);
ws.current = new WebSocket(Constants.WS_ADDRESS + "?auth=" + userSession);
ws.current = socket;
socket.onopen = () => {
ws.current.onopen = () => {
console.log("connected");
setConnectionBadgeStatus("success");
setIsReady(true);
};
socket.onmessage = (event) => {
ws.current.onmessage = (event) => {
const data = JSON.parse(event.data);
console.log("received message", data);
@ -301,7 +301,6 @@ export function WebSocketProvider({
break;
case ReceivedMessagesCommands.UpdateScannerLastUsed:
console.log("Received", body);
setScanners((arr) => {
const newArr = [...arr];
@ -313,7 +312,6 @@ export function WebSocketProvider({
});
break;
case ReceivedMessagesCommands.TaskLocked:
console.log("taskLocked", body);
setGroupTasksSteps((arr) => {
const newArr = [...arr];
@ -343,13 +341,21 @@ export function WebSocketProvider({
return newArr;
});
break;
case ReceivedMessagesCommands.UpdateGroupTaskStepUserInputValue:
const foundInput = document.getElementById(body.element);
if (foundInput) {
setNativeValue(foundInput, body.value);
}
break;
default:
console.error("unknown command", cmd);
break;
}
};
socket.onclose = (event) => {
ws.current.onclose = (event) => {
setIsReady(false);
setConnectionBadgeStatus("error");
console.log("closed", event);
@ -374,7 +380,8 @@ export function WebSocketProvider({
useEffect(() => {
connect();
return () => socket.close();
//return () => socket.close();
return () => ws.current.close();
}, []);
const SendSocketMessage = (cmd, body) => {
@ -406,6 +413,33 @@ export function WebSocketProvider({
</WebSocketContext.Provider>
);
}
/*
function doEvent(obj, event) {
var event = new Event(event, { target: obj, bubbles: true });
event.simulated = true;
let tracker = obj._valueTracker;
if (tracker) {
tracker.setValue(lastValue);
}
return obj ? obj.dispatchEvent(event) : false;
} */
// https://stackoverflow.com/a/52486921
function setNativeValue(element, value) {
let lastValue = element.value;
element.value = value;
let event = new Event("input", { target: element, bubbles: true });
// React 15
event.simulated = true;
// React 16
let tracker = element._valueTracker;
if (tracker) {
tracker.setValue(lastValue);
}
element.dispatchEvent(event);
}
export function FormatDatetime(datetime) {
if (datetime === "0001-01-01T00:00:00Z") {