import { Constants, GetUuid } from "../utils"; // commands received from the backend server export const ReceivedMessagesCommands = { InitUserSocketConnection: 1, UpdateConnectedUsers: 2, NewGroupTaskStarted: 3, NewGroupTaskStep: 4, UpdateGroupTaskStep: 5, UpdateGroupTask: 6, ReloadingGroupTasks: 7, GroupTasksReloaded: 8, UpdateUserSessions: 9, UpdateAllUsersUserAvatar: 10, NewScanner: 11, DeleteScanner: 12, UpdateScannerUsedByUserId: 13, ScanResult: 14, UpdateScannerLastUsed: 15, TaskLocked: 16, TaskUnlocked: 17, UserProfileUpdated: 18, AdminAreaNewRoleCreated: 19, AdminAreaRoleUpdated: 20, AdminAreaUpdateRoleSortingOrder: 21, AdminAreaRoleDeleted: 22, AllUsersUserRoleUpdated: 23, RolePermissionsUpdated: 24, ErrorNoPermissions: 25, AllUsersNewUserCreated: 26, AllUsersUserDeleted: 27, AllUsersUserDeactivation: 28, NewUserApiKeyCreated: 30, DeletedUserApiKey: 31, NewApiKeyUsageCount: 32, InstallingPythonPackages: 33, InstallingPythonPackagesFailed: 34, InstallingPythonPackagesFinished: 35, InstallingGlobalPythonPackages: 36, InstallingGlobalPythonPackagesFailed: 37, InstallingGlobalPythonPackagesFinished: 38, UpdateUsers: 39, CheckingForGroupTasksCategoryGroupChanges: 40, NewNotification: 41, AllNotificationsDeleted: 42, OneNotificationDeleted: 43, AdminAreaManageCheckedForAvailableCategories: 44, }; // commands sent to the backend server export const SentMessagesCommands = { StartGroupTasks: 1, TaskFailedTryAgainRunTaskStep: 2, TaskContinueTaskStep: 3, ReloadGroupTasks: 4, TaskLocking: 5, UpdateUserProfile: 6, AdminAreaCreateNewRole: 7, AdminAreaUpdateRole: 8, AdminAreaUpdateRoleSortingOrder: 9, AdminAreaDeleteRole: 10, AllUsersUpdateUserRole: 11, AllUsersCreateNewUser: 12, AllUsersDeleteUser: 13, AllUsersUserDeactivation: 14, ScannersUseScanners: 15, ScannersDisconnectScanner: 16, HandleUserActionTaskStep: 18, CreateNewUserApiKey: 19, DeleteUserApiKey: 20, GroupTasksInstallPythonPackages: 21, GroupTasksInstallGlobalPythonPackages: 22, SubscribeToTopic: 23, DeleteAllNotifications: 24, DeleteOneNotification: 25, AdminAreaManageCheckWhichCategoriesAreAvailable: 26, }; /* This ID is needed because the message is sent to all clients connected to the backend server when a task is locked and unlocked. With this ID the client checks if the respective browser tab of the user is the initiator of the lock. */ export const GroupTasksStepsLockedAndUserUpdateInputValueRememberId = GetUuid(); export function handleWebSocketMessage( event, navigate, notificationApi, sideBarContext, appContext, headerContext, groupTasksContext, userProfileContext, adminAreaRolesContext, usersContext ) { const data = JSON.parse(event.data); const cmd = data.Cmd; const body = data.Body; switch (cmd) { case ReceivedMessagesCommands.UpdateConnectedUsers: sideBarContext.setConnectedUsers(body); break; case ReceivedMessagesCommands.NewGroupTaskStarted: // add new group task to list and remove latest group task if list length will be greater than 5 if (groupTasksContext.paginationPageRef.current === 1) { groupTasksContext.setGroupTasks((arr) => { const newArr = [...arr]; if ( newArr.length === Constants.GLOBALS.GROUP_TASKS_PAGINATION_LIMIT ) { newArr.pop(); } newArr.unshift(body.GroupTask); return newArr; }); } groupTasksContext.setTotalPages(body.TotalPages); if ( body.GroupTask.RememberId === groupTasksContext.startGroupTasksOpenModalRememberIdRef.current ) { navigate( `${Constants.ROUTE_PATHS.GROUP_TASKS}${body.GroupTask.Category}/view/${body.GroupTask.Id}` ); } break; case ReceivedMessagesCommands.NewGroupTaskStep: groupTasksContext.setGroupTasksSteps((arr) => [...arr, body]); scrollToNextStep(body.GroupTasksId, body.Step); break; case ReceivedMessagesCommands.UpdateGroupTaskStep: groupTasksContext.setGroupTasksSteps((arr) => { const newArr = [...arr]; const stepIndex = arr.findIndex((arr1) => arr1.Id === body.Id); if (stepIndex !== -1) { newArr[stepIndex] = body; } return newArr; }); scrollToNextStep(body.GroupTasksId, body.Step); break; case ReceivedMessagesCommands.UpdateGroupTask: groupTasksContext.setGroupTasks((arr) => { const newArr = [...arr]; const groupTaskIndex = arr.findIndex((arr1) => arr1.Id === body.Id); if (groupTaskIndex !== -1) { newArr[groupTaskIndex] = body; } return newArr; }); break; case ReceivedMessagesCommands.ReloadingGroupTasks: notificationApi["warning"]({ message: `Group ${body} is reloading`, duration: 2, }); break; case ReceivedMessagesCommands.GroupTasksReloaded: if (body.RemovedCategory !== undefined) { groupTasksContext.setCategoryGroup((arr) => arr.filter((arr1) => arr1.category !== body.RemovedCategory) ); appContext.setUserPermissions((arr) => arr.filter((arr1) => arr1 !== body.RemovedCategory) ); notificationApi["info"]({ message: `Category ${body.RemovedCategory} was removed`, duration: 2, }); break; } groupTasksContext.setCategoryGroup((categoryGroup) => ({ ...categoryGroup, groups: body.CategoryGroups, })); notificationApi["success"]({ message: `Group ${body.Category} reloaded`, duration: 2, }); break; case ReceivedMessagesCommands.UpdateUserSessions: //setUser((arr) => ({ ...arr, Sessions: body })); //userProfileContext.setUserProfile((arr) => ({ ...arr, Sessions: body })); userProfileContext.setSessions(body); break; case ReceivedMessagesCommands.UpdateAllUsersUserAvatar: /*setAllUsers((arr) => { const newArr = [...arr]; newArr[arr.findIndex((arr1) => arr1.Id === body.UserId)].Avatar = body.Avatar; return newArr; });*/ if (appContext.userId.current === body.UserId) { sideBarContext.setAvatar(body.Avatar); /* userProfileContext.setUserProfile((user) => ({ ...user, avatar: body.Avatar, })); */ } usersContext.setUsers((arr) => { const newArr = [...arr]; const arrIndex = arr.findIndex((arr1) => arr1.Id === body.UserId); if (arrIndex !== -1) { newArr[arrIndex].Avatar = body.Avatar; } return newArr; }); /* appContext.setUsers((arr) => { const newArr = [...arr]; newArr[arr.findIndex((arr1) => arr1.Id === body.UserId)].Avatar = body.Avatar; return newArr; }); */ break; /*case ReceivedMessagesCommands.NewScanner: setScanners((arr) => [...arr, body]); break; case ReceivedMessagesCommands.DeleteScanner: setScanners((arr) => arr.filter((arr) => arr.Id !== body.Id)); break; */ /*case ReceivedMessagesCommands.UpdateScannerUsedByUserId: setScanners((arr) => { const newArr = [...arr]; newArr[ arr.findIndex((arr1) => arr1.Id === body.ScannerId) ].UsedByUserId = body.UsedByUserId; return newArr; }); break;*/ /* case ReceivedMessagesCommands.ScanResult: const decodedScanResult = DecodedBase64ToString(body); if (decodedScanResult === "" || decodedScanResult === null) { notificationApi["error"]({ message: `Failed to decode scan result`, description: "See in developer console", }); console.error( "Received scan result: ", body, "Decoded result: ", decodedScanResult ); break; } notificationApi["info"]({ message: `Scan Result`, description: decodedScanResult, }); new Audio( `${Constants.STATIC_CONTENT_ADDRESS}sounds/scan_result.mp3` ).play(); break; case ReceivedMessagesCommands.UpdateScannerLastUsed: setScanners((arr) => { const newArr = [...arr]; newArr[arr.findIndex((arr1) => arr1.Id === body.ScannerId)].LastUsed = body.LastUsed; return newArr; }); break;*/ case ReceivedMessagesCommands.TaskLocked: if ( body.rememberId === GroupTasksStepsLockedAndUserUpdateInputValueRememberId ) break; groupTasksContext.setGroupTasksSteps((arr) => { const newArr = [...arr]; const stepIndex = arr.findIndex( (arr1) => arr1.GroupTasksId === body.groupTaskId && arr1.Step === body.step ); if (stepIndex !== -1) { newArr[stepIndex].LockedByUserId = body.lockedByUserId; } return newArr; }); // update input value if (body.inputType === "text") { // html based DOM manipulation const foundInput = document.getElementById(body.element); if (foundInput) { // this timeout is needed because the previous useState for the lockedByUserId takes some milliseconds to complete setTimeout(() => setNativeValue(foundInput, body.value), 50); } } else if (body.inputType === "select") { groupTasksContext.setSelectInputs((prev) => { const newInputs = { ...prev }; if (newInputs[body.parameterName] === undefined) { newInputs[body.parameterName] = { value: body.value, data: body.data, }; } else { newInputs[body.parameterName].value = body.value; newInputs[body.parameterName].data = body.data; } //newInputs[body.parameterName].value = body.value; return newInputs; }); } else { console.error("Unknown input type"); } // update group task step as html based DOM manipulation only works if user has no other modal open groupTasksContext.setGroupTasksSteps((arr) => { const newArr = [...arr]; const stepIndex = arr.findIndex( (arr1) => arr1.GroupTasksId === body.groupTaskId && arr1.Step === body.step ); if (stepIndex === -1) return newArr; let inputs = {}; if (newArr[stepIndex].Inputs !== "") { inputs = JSON.parse(newArr[stepIndex].Inputs); } let parameterFound = false; const inputKeys = Object.keys(inputs); for (let i = 0; i < inputKeys.length; i++) { if (inputKeys[i] === body.parameterName) { inputs[inputKeys[i]].value = body.value; parameterFound = true; break; } } if (!parameterFound) { let newObj = { value: body.value, }; if (body.data !== undefined) { newObj.data = body.data; } inputs[body.parameterName] = newObj; } else { inputs[body.parameterName].value = body.value; if (body.data !== undefined) { inputs[body.parameterName].data = body.data; } } return newArr; }); break; case ReceivedMessagesCommands.TaskUnlocked: if ( body.rememberId === GroupTasksStepsLockedAndUserUpdateInputValueRememberId ) break; groupTasksContext.setGroupTasksSteps((arr) => { const newArr = [...arr]; const stepIndex = arr.findIndex( (arr1) => arr1.GroupTasksId === body.GroupTaskId && arr1.Step === body.Step ); if (stepIndex !== -1) { newArr[stepIndex].LockedByUserId = ""; } return newArr; }); break; case ReceivedMessagesCommands.UserProfileUpdated: // feedback message for the user who has changed his profile if (body.Result !== undefined) { if (body.Result === 0) { notificationApi["error"]({ message: `Profile couldn't be updated`, description: "Username already in use", }); } else if (body.Result === 1) { notificationApi["error"]({ message: `Profile couldn't be updated`, description: "Email already in use", }); } else if (body.Result === 2) { notificationApi["error"]({ message: `Profile couldn't be updated`, description: "Provided password is incorrect", }); } } if (body.Changes !== undefined) { if (body.Changes.Username !== undefined) { if (appContext.userId.current === body.UserId) { sideBarContext.setUsername(body.Changes.Username); } usersContext.setUsers((arr) => { const newArr = [...arr]; const arrIndex = arr.findIndex((user) => user.Id === body.UserId); if (arrIndex !== -1) { newArr[arrIndex].Username = body.Changes.Username; } return newArr; }); } if (body.Changes.Email !== undefined) { userProfileContext.setEmail(body.Changes.Email); } } break; case ReceivedMessagesCommands.AdminAreaNewRoleCreated: adminAreaRolesContext.setRoles((arr) => [...arr, body]); adminAreaRolesContext.setRolesPermissions((arr) => [ ...arr, { RoleId: body.Id, Permissions: [] }, ]); break; case ReceivedMessagesCommands.AdminAreaRoleUpdated: adminAreaRolesContext.setRoles((arr) => { const newArr = [...arr]; const arrIndex = arr.findIndex((arr1) => arr1.Id === body.RoleId); if (arrIndex !== -1) { if (body.Changes.DisplayName !== undefined) { newArr[arrIndex].DisplayName = body.Changes.DisplayName; } if (body.Changes.Description !== undefined) { newArr[arrIndex].Description = body.Changes.Description; } } return newArr; }); if ( body.Changes.AddedPermissions !== undefined || body.Changes.RemovedPermissions !== undefined ) { adminAreaRolesContext.setRolesPermissions((arr) => { const newArr = [...arr]; const roleIndex = arr.findIndex( (item) => item.RoleId === body.RoleId ); if (roleIndex !== -1) { if (body.Changes.AddedPermissions !== undefined) { if (newArr[roleIndex].Permissions === null) { newArr[roleIndex].Permissions = body.Changes.AddedPermissions; } else { newArr[roleIndex].Permissions = newArr[ roleIndex ].Permissions.concat(body.Changes.AddedPermissions); } } if (body.Changes.RemovedPermissions !== undefined) { newArr[roleIndex].Permissions = newArr[ roleIndex ].Permissions.filter( (permission) => !body.Changes.RemovedPermissions.includes(permission) ); } } return newArr; }); } if (body.Result !== undefined) { if (body.Result.DisplayName !== undefined) { if (body.Result.DisplayName === 1) { notificationApi["error"]({ message: `Display name could be changed`, description: `Display name already in use`, }); } } } break; case ReceivedMessagesCommands.AdminAreaUpdateRoleSortingOrder: adminAreaRolesContext.setRoles((arr) => { const newArr = [...arr]; const updatedRoleIndex = newArr.findIndex( (role) => role.Id === body.RoleId ); if (updatedRoleIndex === -1) return newArr; const currentSortingOrder = newArr[updatedRoleIndex].SortingOrder; if (body.Direction === 0) { newArr[updatedRoleIndex].SortingOrder = newArr[updatedRoleIndex].SortingOrder - 1; } else { newArr[updatedRoleIndex].SortingOrder = newArr[updatedRoleIndex].SortingOrder + 1; } const newSortingOrder = newArr[updatedRoleIndex].SortingOrder; for (let i = 0; i < newArr.length; i++) { if (newArr[i].Id !== newArr[updatedRoleIndex].Id) { if (newArr[i].SortingOrder === newSortingOrder) { newArr[i].SortingOrder = currentSortingOrder; } else if ( newArr[i].SortingOrder < currentSortingOrder && newArr[i].SortingOrder >= newSortingOrder ) { newArr[i].SortingOrder = newArr[i].SortingOrder + 1; } else if ( newArr[i].SortingOrder > currentSortingOrder && newArr[i].SortingOrder <= newSortingOrder ) { newArr[i].SortingOrder = newArr[i] - 1; } } } newArr.sort((a, b) => a.SortingOrder - b.SortingOrder); return newArr; }); break; case ReceivedMessagesCommands.AdminAreaRoleDeleted: adminAreaRolesContext.setRoles((arr) => { let newArr = [...arr]; const deletedRole = newArr.find((r) => r.Id === body.RoleId); newArr = newArr.filter((role) => role.Id !== body.RoleId); for (let i = 0; i < newArr.length; i++) { if (newArr[i].SortingOrder > deletedRole.SortingOrder) { newArr[i].SortingOrder = newArr[i].SortingOrder - 1; } } return newArr; }); adminAreaRolesContext.setRolesPermissions((arr) => { let newArr = [...arr]; newArr = newArr.filter( (rolePermission) => rolePermission.RoleId !== body.RoleId ); return newArr; }); break; case ReceivedMessagesCommands.AllUsersUserRoleUpdated: usersContext.setUsers((arr) => { const newArr = [...arr]; const arrIndex = arr.findIndex((user) => user.Id === body.UserId); if (arrIndex !== -1) { newArr[arrIndex].RoleId = body.RoleId; } return newArr; }); if (body.Permissions !== undefined) { appContext.setUserPermissions(() => body.Permissions === null ? [] : body.Permissions ); /*setUser((user) => { const updatedUser = { ...user }; if (body.Permissions === null) { updatedUser.Permissions = []; } else { updatedUser.Permissions = body.Permissions; } return updatedUser; }); */ } break; case ReceivedMessagesCommands.RolePermissionsUpdated: if ( body.Changes.AddedPermissions !== undefined || body.Changes.RemovedPermissions !== undefined ) { appContext.setUserPermissions((arr) => { let newArr = [...arr]; if (body.Changes.AddedPermissions !== undefined) { newArr = newArr.concat(body.Changes.AddedPermissions); } if (body.Changes.RemovedPermissions !== undefined) { newArr = newArr.filter( (permission) => !body.Changes.RemovedPermissions.includes(permission) ); } return newArr; }); } sideBarContext.setAvailableCategories(body.AvailableCategories); break; case ReceivedMessagesCommands.ErrorNoPermissions: notificationApi["error"]({ message: `No permissions`, description: `Please contact the administrator`, }); break; case ReceivedMessagesCommands.AllUsersNewUserCreated: if (body.Result !== undefined) { if (body.Result === 0) { notificationApi["error"]({ message: `User could not be created`, description: `Username already in use`, }); } if (body.Result === 1) { notificationApi["error"]({ message: `User could not be created`, description: `Email already in use`, }); } break; } usersContext.setUsers((arr) => [ ...arr, { Id: body.Id, RoleId: body.RoleId, Username: body.Username, ConnectionStatus: body.ConnectionStatus, Deactivated: body.Deactivated, }, ]); break; case ReceivedMessagesCommands.AllUsersUserDeleted: usersContext.setUsers((arr) => { let newArr = [...arr]; newArr = newArr.filter((user) => user.Id !== body.UserId); return newArr; }); /* if (body.ScannerId !== "") { setScanners((arr) => { let newArr = [...arr]; newArr[ newArr.findIndex((scanner) => scanner.Id === body.ScannerId) ].UsedByUserId = ""; return newArr; }); } */ break; case ReceivedMessagesCommands.AllUsersUserDeactivation: usersContext.setUsers((arr) => { let newArr = [...arr]; const arrIndex = newArr.findIndex((user) => user.Id === body.UserId); if (arrIndex !== -1) { newArr[arrIndex].Deactivated = body.Deactivated; } return newArr; }); break; case ReceivedMessagesCommands.NewUserApiKeyCreated: /*userProfileContext.setUserProfile((user) => ({ ...user, ApiKeys: [...user.ApiKeys, body], })); */ userProfileContext.setApiKeys((arr) => [...arr, body]); /*setUser((user) => { const updatedUser = { ...user }; updatedUser.ApiKeys.push(body); return updatedUser; });*/ break; case ReceivedMessagesCommands.DeletedUserApiKey: /*userProfileContext.setUserProfile((user) => ({ ...user, ApiKeys: user.ApiKeys.filter((apiKey) => apiKey.Id !== body), })); */ userProfileContext.setApiKeys((arr) => arr.filter((apiKey) => apiKey.Id !== body) ); /*setUser((user) => { const updatedUser = { ...user }; updatedUser.ApiKeys = updatedUser.ApiKeys.filter( (apiKey) => apiKey.Id !== body ); return updatedUser; }); */ break; case ReceivedMessagesCommands.NewApiKeyUsageCount: userProfileContext.setApiKeys((arr) => { const newArr = [...arr]; const arrIndex = newArr.findIndex((apiKey) => apiKey.Id === body.Id); if (arrIndex !== -1) { newArr[arrIndex].UsageCount = body.UsageCount; newArr[arrIndex].LastUsed = body.LastUsed; } return newArr; }); /*userProfileContext.setUserProfile((user) => { const updatedUser = { ...user }; const arrIndex = updatedUser.ApiKeys.findIndex( (apiKey) => apiKey.Id === body.Id ); if (arrIndex !== -1) { updatedUser.ApiKeys[arrIndex].UsageCount = body.UsageCount; updatedUser.ApiKeys[arrIndex].LastUsed = body.LastUsed; } return updatedUser; }); */ /*setUser((user) => { const updatedUser = { ...user }; const arrIndex = updatedUser.ApiKeys.findIndex( (apiKey) => apiKey.Id === body.Id ); if (arrIndex !== -1) { updatedUser.ApiKeys[arrIndex].UsageCount = body.UsageCount; updatedUser.ApiKeys[arrIndex].LastUsed = body.LastUsed; } return updatedUser; }); */ break; case ReceivedMessagesCommands.InstallingPythonPackages: notificationApi["info"]({ message: `Installing python packages for ${body.GroupId} of ${body.Category}`, description: `This may take a while`, }); break; case ReceivedMessagesCommands.InstallingPythonPackagesFailed: notificationApi["error"]({ message: `Installing python packages for ${body.GroupId} of ${body.Category} failed`, description: `Please check the logs`, }); break; case ReceivedMessagesCommands.InstallingPythonPackagesFinished: notificationApi["success"]({ message: `Installing python packages for ${body.GroupId} of ${body.Category} finished`, description: `You can now start the group task`, }); break; case ReceivedMessagesCommands.InstallingGlobalPythonPackages: notificationApi["info"]({ message: `Installing global python packages`, description: `This may take a while`, }); break; case ReceivedMessagesCommands.InstallingGlobalPythonPackagesFailed: notificationApi["error"]({ message: `Installing global python packages failed`, description: `Please check the logs`, }); break; case ReceivedMessagesCommands.InstallingGlobalPythonPackagesFinished: notificationApi["success"]({ message: `Installing global python packages finished`, description: `You can now continue with the work`, }); break; case ReceivedMessagesCommands.UpdateUsers: usersContext.setUsers((arr) => { const newArr = [...arr]; const arrIndex = arr.findIndex((arr1) => arr1.Id === body.UserId); if (arrIndex !== -1) { newArr[arrIndex].ConnectionStatus = body.ConnectionStatus; newArr[arrIndex].LastOnline = body.LastOnline; } return newArr; }); break; case ReceivedMessagesCommands.CheckingForGroupTasksCategoryGroupChanges: notificationApi["warning"]({ message: `Checking for category group changes`, description: `This may take a while`, }); break; case ReceivedMessagesCommands.NewNotification: if (headerContext.paginationPageRef.current === 1) { headerContext.setNotificationResponse((obj) => { if (obj === null) return obj; const newArr = [...obj.Notifications]; if ( newArr.length === Constants.GLOBALS.NOTIFICATIONS_PAGINATION_LIMIT ) { newArr.pop(); } newArr.unshift(body.Notification); obj.Notifications = newArr; obj.TotalPages = body.TotalPages; return obj; }); } headerContext.setTotalNotifications( (totalNotifications) => totalNotifications + 1 ); break; case ReceivedMessagesCommands.AllNotificationsDeleted: headerContext.setNotificationResponse(null); headerContext.setTotalNotifications(0); headerContext.setPaginationPage(1); break; case ReceivedMessagesCommands.OneNotificationDeleted: headerContext.setTotalNotifications( (totalNotifications) => totalNotifications - 1 ); headerContext.setNotificationResponse((obj) => { if (obj === null) return obj; const newArr = [...obj.Notifications]; const arrIndex = newArr.findIndex( (notification) => notification.Id === body ); if (arrIndex !== -1) { newArr.splice(arrIndex, 1); } obj.Notifications = newArr; if ( newArr.length === 0 && headerContext.paginationPageRef.current > 1 ) { const newPaginationPage = headerContext.paginationPageRef.current - 1; headerContext.setPaginationPage(newPaginationPage); headerContext.paginationPageRef.current = newPaginationPage; } return obj; }); break; case ReceivedMessagesCommands.AdminAreaManageCheckedForAvailableCategories: if (body === null) { notificationApi["success"]({ message: `Checking for available categories finished`, }); break; } if (body.MasterRolePermissions !== undefined) { adminAreaRolesContext.setRolesPermissions((arr) => { const newArr = [...arr]; const roleIndex = arr.findIndex( (item) => item.RoleId === body.MasterRoleId ); if (roleIndex !== -1) { newArr[roleIndex].Permissions = body.MasterRolePermissions; } return newArr; }); } if (body.AvailableCategories !== undefined) { sideBarContext.setAvailableCategories( body.AvailableCategories === null ? [] : body.AvailableCategories ); } // only for the user who is member of the master role if (body.UserPermissions !== undefined) { appContext.setUserPermissions( body.UserPermissions === null ? [] : body.UserPermissions ); } break; default: console.error("unknown command", cmd); break; } } function scrollToNextStep(groupTaskId, step) { setTimeout( () => document .getElementById(`${groupTaskId}-scroll-${step - 1}`) ?.scrollIntoView({ behavior: "smooth" }), 200 ); } // 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); }