diff --git a/.gitignore b/.gitignore index 3d49fa09..fecfed20 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,4 @@ # Logs -logs -*.log npm-debug.log* yarn-debug.log* yarn-error.log* @@ -88,11 +86,9 @@ typings/ # Electron-Forge out/ dist/ +.idea/ # Project - -.idea/ -*.sqlite* -release-builds/ -/admin/database.sqlite* -/logo.* \ No newline at end of file +/serverDevLogs/* +/serverDevImages/* +/paradise-development-database.sqlite* \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json index bd83ce61..c7ee8742 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -15,6 +15,13 @@ "type": "node", "request": "launch", "cwd": "${workspaceRoot}/server", + "env": { + "NODE_ENV": "development", + "PARADISE_LOG_PATH": "${workspaceRoot}/serverDevLogs", + "PARADISE_IMAGE_PATH": "${workspaceRoot}/serverDevImages", + "PARADISE_DATABASE_PATH": "${workspaceRoot}/paradise-development-database.sqlite" + }, + "outputCapture": "std", "runtimeExecutable": "npm", "runtimeArgs": ["run", "develop"], "preLaunchTask": "NPM Install - Server" diff --git a/.vscode/settings.json b/.vscode/settings.json index c4f28ea5..2503bc5b 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -14,6 +14,7 @@ "Gilles", "Logline", "Mantine", + "maxsize", "Millis", "paradisepi", "portfinder", @@ -21,7 +22,9 @@ "prefade", "qrcode", "tagline", + "tailable", "typeorm", + "undefinedbuild", "xair", "xremote" ], diff --git a/balena.yml b/balena.yml index e91413ee..56798e28 100644 --- a/balena.yml +++ b/balena.yml @@ -32,4 +32,4 @@ data: - intel-nuc - generic-amd64 - generic-aarch64 -version: 3.0.4 +version: 3.1.0 diff --git a/client/Components/ControlPanel/Fader.tsx b/client/Components/ControlPanel/Fader.tsx index dc0d0af0..68c02a09 100644 --- a/client/Components/ControlPanel/Fader.tsx +++ b/client/Components/ControlPanel/Fader.tsx @@ -1,5 +1,6 @@ -import React from 'react' import { MantineTheme, Slider } from '@mantine/core' +import { useTimeout } from '@mantine/hooks' +import React from 'react' /** * Notches on the fader used for the metering, available in decibels @@ -72,44 +73,58 @@ export const Fader = (props: { meterValue: number value: number | false onChange: (value: number) => void -}) => ( - (!props.disabled ? props.onChange(value) : false)} - value={props.value !== false ? props.value : null} - radius={'lg'} - showLabelOnHover={false} - size={'xl'} - marks={percentageMarks} - step={1} - min={0} - max={100} - precision={0} - label={() => null} - styles={theme => ({ - track: { - '&:before': { - background: metering(theme, props.meterValue), + onSettle: () => void +}) => { + // The timeout function debounces the fader for use in the history module, to avoid filling up the logs with every value. This calls a function after a second of inactivity on the fader. + const { start, clear } = useTimeout(() => props.onSettle(), 1000, { + autoInvoke: false, + }) + return ( + { + if (props.disabled) return false + else { + clear() + start() + props.onChange(value) + } + }} + value={props.value !== false ? props.value : null} + radius={'lg'} + showLabelOnHover={false} + size={'xl'} + marks={percentageMarks} + step={1} + min={0} + max={100} + precision={0} + label={() => null} + styles={theme => ({ + track: { + '&:before': { + background: metering(theme, props.meterValue), + }, + }, + bar: { + backgroundColor: 'transparent', + }, + mark: { + border: 0, + height: 12, + width: 1, }, - }, - bar: { - backgroundColor: 'transparent', - }, - mark: { - border: 0, - height: 12, - width: 1, - }, - thumb: { - display: 'block', - height: props.disabled ? '1em' : '2em', - width: props.disabled ? '0.5em' : '1em', - backgroundColor: 'white', - }, - root: { - paddingTop: '1em', - paddingBottom: '2em', - }, - })} - /> -) + thumb: { + display: 'block', + height: props.disabled ? '1em' : '2em', + width: props.disabled ? '0.5em' : '1em', + backgroundColor: 'white', + }, + root: { + paddingTop: '1em', + paddingBottom: '2em', + }, + })} + /> + ) +} diff --git a/client/Components/ControlPanel/PresetFaders.tsx b/client/Components/ControlPanel/PresetFaders.tsx index c42c89ba..e0d43136 100644 --- a/client/Components/ControlPanel/PresetFaders.tsx +++ b/client/Components/ControlPanel/PresetFaders.tsx @@ -50,6 +50,13 @@ export const PresetFaders = (props: { faders: Array }) => { value: val / 100, }) } + onSettle={() => + ApiCall.post('/faders/log', { + address: '/' + faderString + '/mix/fader', + id: fader.id, + name: fader.name, + }) + } /> ) diff --git a/client/Components/ErrorBoundary.tsx b/client/Components/ErrorBoundary.tsx index 804522cc..38b05fea 100644 --- a/client/Components/ErrorBoundary.tsx +++ b/client/Components/ErrorBoundary.tsx @@ -1,5 +1,4 @@ -import { Accordion, Box, Button, Code, Container, Divider, Text, Title } from '@mantine/core' -import { QRCodeSVG } from 'qrcode.react' +import { Button, Container, Title } from '@mantine/core' import React, { Component, ErrorInfo, ReactNode } from 'react' interface Props { @@ -47,7 +46,7 @@ class ErrorBoundary extends Component { - + diff --git a/client/Pages/Admin/Configuration.tsx b/client/Pages/Admin/Configuration.tsx index 66c02ed9..bacca693 100644 --- a/client/Pages/Admin/Configuration.tsx +++ b/client/Pages/Admin/Configuration.tsx @@ -1,13 +1,15 @@ import { Tabs } from '@mantine/core' -import { FaDatabase } from '@react-icons/all-files/fa/FaDatabase' import { FaDrum } from '@react-icons/all-files/fa/FaDrum' +import { FaHistory } from '@react-icons/all-files/fa/FaHistory' import { FaLightbulb } from '@react-icons/all-files/fa/FaLightbulb' import { FaPaintBrush } from '@react-icons/all-files/fa/FaPaintBrush' +import { FaStethoscope } from '@react-icons/all-files/fa/FaStethoscope' import { FaTools } from '@react-icons/all-files/fa/FaTools' import React, { ReactNode } from 'react' -import { DatabaseAndLogsConfigurationPage } from './ModuleConfiguration/DatabaseAndLogs' +import { DiagnosticsConfigurationPage } from './ModuleConfiguration/Diagnostics' import { E131ModuleConfigurationPage } from './ModuleConfiguration/E131' import { GeneralConfigurationPage } from './ModuleConfiguration/General' +import { HistoryConfigurationPage } from './ModuleConfiguration/History' import { OSCModuleConfigurationPage } from './ModuleConfiguration/OSC' import { ScreensaverConfigurationPage } from './ModuleConfiguration/Screensaver' @@ -25,15 +27,18 @@ export const ConfigurationPage = () => { }> Screensaver - }> - Database & Logs - }> sACN (E1.31) }> OSC + }> + History Recording + + }> + Diagnostics + @@ -45,11 +50,6 @@ export const ConfigurationPage = () => { - - - - - @@ -60,6 +60,16 @@ export const ConfigurationPage = () => { + + + + + + + + + + ) diff --git a/client/Pages/Admin/ModuleConfiguration/DatabaseAndLogs.tsx b/client/Pages/Admin/ModuleConfiguration/DatabaseAndLogs.tsx deleted file mode 100644 index 875a565c..00000000 --- a/client/Pages/Admin/ModuleConfiguration/DatabaseAndLogs.tsx +++ /dev/null @@ -1,64 +0,0 @@ -import React, { useState } from 'react' -import { Alert, Blockquote, Button, Divider, Modal, Container } from '@mantine/core' -import { FaExclamationTriangle } from '@react-icons/all-files/fa/FaExclamationTriangle' -import { useAppSelector } from '../../../apis/redux/mainStore' -import { Prism } from '@mantine/prism' -import { useViewportSize } from '@mantine/hooks' - -const Logs = () => { - const { width } = useViewportSize() - const logs = useAppSelector(state => state.logs.logs) - return ( - - - {logs.map(logLine => JSON.stringify(JSON.parse(logLine), null, 2)).join('\n')} - - - ) -} -const Database = () => { - const [showModal, setShowModal] = useState(false) - return ( - <> - - - - - setShowModal(false)} opened={showModal} title="Upload new Database"> - } title="Danger" color="gray" my="sm"> - Ensure you are uploading a valid backup file from Paradise as this file will not be checked to - ensure it is a valid backup file - if this is not a backup file, you will lose all data and will - have to uninstall Paradise and install it again. Please also ensure no other users are currently - using the system, as all lighting and sound will be lost. - -
- - -
-
- - - - - ) -} -export const DatabaseAndLogsConfigurationPage = () => ( - <> - - - - -) diff --git a/client/Pages/Admin/ModuleConfiguration/Diagnostics.tsx b/client/Pages/Admin/ModuleConfiguration/Diagnostics.tsx new file mode 100644 index 00000000..025428d3 --- /dev/null +++ b/client/Pages/Admin/ModuleConfiguration/Diagnostics.tsx @@ -0,0 +1,36 @@ +import { Button, Container, Divider } from '@mantine/core' +import { useViewportSize } from '@mantine/hooks' +import { Prism } from '@mantine/prism' +import React from 'react' +import { useAppSelector } from '../../../apis/redux/mainStore' + +const Logs = () => { + const { width } = useViewportSize() + const logs = useAppSelector(state => state.logs.logs) + return ( + + + {logs.map(logLine => JSON.stringify(JSON.parse(logLine), null, 2)).join('\n')} + + + ) +} +export const DiagnosticsConfigurationPage = () => ( + <> + + + + + + +) diff --git a/client/Pages/Admin/ModuleConfiguration/General.tsx b/client/Pages/Admin/ModuleConfiguration/General.tsx index afcb8293..466f6539 100644 --- a/client/Pages/Admin/ModuleConfiguration/General.tsx +++ b/client/Pages/Admin/ModuleConfiguration/General.tsx @@ -1,10 +1,59 @@ -import { Box, Button, Checkbox, Divider, Loader, LoadingOverlay, PasswordInput, Text, Title } from '@mantine/core' +import { + Alert, + Box, + Button, + Checkbox, + Divider, + Loader, + LoadingOverlay, + Modal, + PasswordInput, + Text, + Title, +} from '@mantine/core' import { useForm } from '@mantine/form' import { RichTextEditor } from '@mantine/rte' +import { FaExclamationTriangle } from '@react-icons/all-files/fa/FaExclamationTriangle' import { FaSave } from '@react-icons/all-files/fa/FaSave' import React, { useEffect, useState } from 'react' import { useAppSelector } from '../../../apis/redux/mainStore' import { ApiCall } from '../../../apis/wrapper' +const Database = () => { + const [showModal, setShowModal] = useState(false) + return ( + <> + + + + + setShowModal(false)} opened={showModal} title="Upload new Database"> + } title="Danger" color="gray" my="sm"> + Ensure you are uploading a valid backup file from Paradise as this file will not be checked to + ensure it is a valid backup file - if this is not a backup file, you will lose all data and will + have to uninstall Paradise and install it again. Please also ensure no other users are currently + using the system, as all lighting and sound will be lost. + +
+ + +
+
+ + ) +} export const GeneralConfigurationPage = () => { const [loadingOverlayVisible, setLoadingOverlayVisible] = useState(false) @@ -19,7 +68,8 @@ export const GeneralConfigurationPage = () => { fullscreen: false, }, validate: { - adminPin: value => (value == '' ? null : /^\d+$/.test(value) ? null : 'Invalid pin - must only contain numbers'), + adminPin: value => + value == '' ? null : /^\d+$/.test(value) ? null : 'Invalid pin - must only contain numbers', }, }) useEffect(() => { @@ -55,18 +105,11 @@ export const GeneralConfigurationPage = () => { return ( +
- - - {/* You can't set the lock or hide the admin button whilst in electron as it would cause a condition where you can lock yourself out but never get in again */} { style={{ display: 'none' }} {...form.getInputProps('fullscreen', { type: 'checkbox' })} /> - { label="Allow access from Control Panel to Admin" {...form.getInputProps('adminLinkFromControlPanel', { type: 'checkbox' })} /> + + {/* TODO - develop functionality so you can't set the lock or hide the admin button whilst in electron as it would cause a condition where you can lock yourself out but never get in again */} Help Page Text to display on the help page diff --git a/client/Pages/Admin/ModuleConfiguration/History.tsx b/client/Pages/Admin/ModuleConfiguration/History.tsx new file mode 100644 index 00000000..4ff8bb1a --- /dev/null +++ b/client/Pages/Admin/ModuleConfiguration/History.tsx @@ -0,0 +1,83 @@ +import { Box, Button, Checkbox, Loader, LoadingOverlay, MultiSelect } from '@mantine/core' +import { useForm } from '@mantine/form' +import { FaSave } from '@react-icons/all-files/fa/FaSave' +import React, { useEffect, useState } from 'react' +import { useAppSelector } from '../../../apis/redux/mainStore' +import { ApiCall } from '../../../apis/wrapper' + +export const HistoryConfigurationPage = () => { + const [loadingOverlayVisible, setLoadingOverlayVisible] = useState(false) + const historyConfig = useAppSelector(state => (state.database ? state.database.config.history : false)) + const form = useForm({ + initialValues: { + historyEnabled: false, + historyLogParameters: [] as string[], + }, + validate: { + historyLogParameters: value => (true ? null : 'Must be a valid IPv4 address'), + }, + }) + useEffect(() => { + if (historyConfig !== false) { + form.setValues({ + historyEnabled: historyConfig.historyEnabled, + historyLogParameters: historyConfig.historyLogParameters, + }) + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [historyConfig]) + const handleSubmit = (values: typeof form.values) => { + setLoadingOverlayVisible(true) + const valuesForApi = { + historyEnabled: values.historyEnabled, + historyLogParameters: values.historyLogParameters.join(','), + } + ApiCall.post('/config', valuesForApi).then(() => { + setLoadingOverlayVisible(false) + }) + } + if (!historyConfig) return + return ( + + + + + + + + + + + + ) +} diff --git a/client/Pages/Admin/ModuleConfiguration/Screensaver.tsx b/client/Pages/Admin/ModuleConfiguration/Screensaver.tsx index b0aa45a0..4abd6952 100644 --- a/client/Pages/Admin/ModuleConfiguration/Screensaver.tsx +++ b/client/Pages/Admin/ModuleConfiguration/Screensaver.tsx @@ -91,7 +91,11 @@ const UploadLogo = () => {
Only JPEG and PNG can be uploaded
Transparent images with light backgrounds work best -
+