diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md index cc575cdf1..38346a629 100644 --- a/DEVELOPMENT.md +++ b/DEVELOPMENT.md @@ -27,7 +27,7 @@ You can generate a distribution for your OS by running the following steps. From the project root, run the following commands - __Install the project dependencies__ by running `pnpm i` -- __Build the UI and server__ by running `turbo build` +- __Build the UI and server__ by running `turbo build:local` - __Create the package__ by running `turbo dist-win`, `turbo dist-mac` or `turbo dist-linux` The build distribution assets will be at `.apps/electron/dist` diff --git a/README.md b/README.md index 5074f534c..2a059dd77 100644 --- a/README.md +++ b/README.md @@ -65,6 +65,7 @@ More documentation is available [in our docs](https://cpvalente.gitbook.io/ontim - Backstage Info - Public Info - Studio Clock + - Countdown - [Make your own?](#make-your-own-viewer) - [x] Configurable Lower Thirds - [x] Cuesheets with user definable fields @@ -109,11 +110,11 @@ Taking advantage of the integrations, we currently use Ontime with: ### Make your own viewer -Ontime broadcasts its data over WebSockets. This allows you to consume its data outside of the application. +Ontime broadcasts its data over WebSockets. This allows you to consume its data outside the application. Writing a new view for the browser can be done with basic knowledge of HTML + CSS + Javascript (or any other language that can run in the browser).
-See [this repository](https://github.com/cpvalente/ontime-viewer-template) with a small template on +See [this repository](https://github.com/cpvalente/ontime-viewer-template-v2) with a small template on how to get you started and read the docs about the [Websocket API](https://app.gitbook.com/s/-Mc0giSOToAhq0ROd0CR/control-and-feedback/websocket-api) @@ -132,13 +133,7 @@ in [available Docker Hub at getontime/ontime](https://hub.docker.com/r/getontime docker pull getontime/ontime ``` -```bash -# Port 4001 - ontime server port -# Port 8888 - OSC input, bound to localhost IP Address -docker run -p 4001:4001 -p 127.0.0.1:8888:8888/udp --mount type=bind,source="$(pwd)/ontime-db",target=/server/preloaded-db getontime/ontime -``` - -or if running from the docker compose +and use the included docker compose to get started ```bash docker-compose up diff --git a/apps/client/package.json b/apps/client/package.json index 80070172e..1e4a7c2e7 100644 --- a/apps/client/package.json +++ b/apps/client/package.json @@ -20,7 +20,6 @@ "csv-stringify": "^6.2.3", "deepmerge": "^4.3.0", "framer-motion": "^10.10.0", - "luxon": "^3.3.0", "react": "^18.2.0", "react-dom": "^18.2.0", "react-fast-compare": "^3.2.0", diff --git a/apps/client/src/common/components/input/delay-input/DelayInput.module.scss b/apps/client/src/common/components/input/delay-input/DelayInput.module.scss index edad5c2d9..a7cb4f03f 100644 --- a/apps/client/src/common/components/input/delay-input/DelayInput.module.scss +++ b/apps/client/src/common/components/input/delay-input/DelayInput.module.scss @@ -1,13 +1,24 @@ @use '../../../../theme/v2Styles' as *; +$input-font-size: 15px; + .delayInput { display: flex; gap: $element-spacing; align-items: center; - color: $ontime-delay-text; font-size: $text-body-size; + + + .inputField { + font-size: $input-font-size; + letter-spacing: 1px; + max-width: 7em; + padding-left: 16px; + color: $ontime-delay-text + } } -.inputField { - text-align: center; -} \ No newline at end of file +.delayOptions { + display: flex; + flex-direction: column; +} diff --git a/apps/client/src/common/components/input/delay-input/DelayInput.tsx b/apps/client/src/common/components/input/delay-input/DelayInput.tsx index c53ef0994..a9b6681ed 100644 --- a/apps/client/src/common/components/input/delay-input/DelayInput.tsx +++ b/apps/client/src/common/components/input/delay-input/DelayInput.tsx @@ -1,88 +1,134 @@ -import { useCallback, useEffect, useRef, useState } from 'react'; -import { Input } from '@chakra-ui/react'; +import { KeyboardEvent, useEffect, useRef, useState } from 'react'; +import { Input, Radio, RadioGroup } from '@chakra-ui/react'; +import { millisToString } from 'ontime-utils'; -import { clamp } from '../../../utils/math'; +import { useEventAction } from '../../../hooks/useEventAction'; +import { forgivingStringToMillis } from '../../../utils/dateConfig'; import style from './DelayInput.module.scss'; -const inputStyleProps = { - width: 20, - placeholder: '-', - size: 'sm', - color: '#E69056', - variant: 'ontime-filled', - fontSize: '15px', - letterSpacing: '0.3px', -}; - interface DelayInputProps { - submitHandler: (value: number) => void; - value?: number; + eventId: string; + duration: number; } export default function DelayInput(props: DelayInputProps) { - const { submitHandler, value = 0 } = props; - const [_value, setValue] = useState(value); + const { eventId, duration } = props; + const { updateEvent } = useEventAction(); + + const [value, setValue] = useState(''); const inputRef = useRef(null); + // avoid wrong submit on cancel + let ignoreChange = false; useEffect(() => { - if (!value) { + if (typeof duration === undefined) { return; } - setValue(value); - }, [value]); + setValue(millisToString(duration)); + }, [duration]); /** * @description Prepare delay value for update - * @param {string} value string to be parsed + * @param {string} newValue string to be parsed */ - const validate = useCallback( - (newValue?: string) => { - if (newValue === '') setValue(0); - const delayValue = clamp(Number(newValue), -60, 60); - if (delayValue === value) return; - setValue(delayValue); + const validateAndSubmit = (newValue: string) => { + if (ignoreChange) { + ignoreChange = false; + return; + } - submitHandler(delayValue); - }, - [submitHandler, value], - ); + const isNegative = newValue.startsWith('-'); + let newMillis = forgivingStringToMillis(newValue); + + if (isNegative) { + newMillis = newMillis * -1; + } + + if (newMillis === duration) { + return; + } + + submitChange(newMillis); + setValue(millisToString(newMillis)); + }; + + const submitChange = (value: number) => { + updateEvent({ + id: eventId, + duration: value, + }); + }; + + /** + * @description Selects input text on focus + */ + const handleFocus = () => inputRef.current?.select(); /** * @description Handles common keys for submit and cancel * @param {KeyboardEvent} event */ - const onKeyDownHandler = useCallback( - (key: string) => { - if (key === 'Enter') { - inputRef.current?.blur(); - validate(inputRef.current?.value); - } else if (key === 'Escape') { - inputRef.current?.blur(); - setValue(value); - } - }, - [validate, value], - ); + const onKeyDownHandler = (event: KeyboardEvent) => { + if (event.key === 'Enter') { + inputRef.current?.blur(); + validateAndSubmit((event.target as HTMLInputElement).value); + } else if (event.key === 'Tab') { + validateAndSubmit((event.target as HTMLInputElement).value); + } else if (event.key === 'Escape') { + ignoreChange = true; + setValue(millisToString(duration)); + inputRef.current?.blur(); + } + }; - const labelText = `${Math.abs(value) !== 1 ? 'minutes' : 'minute'} ${ - value !== undefined && value >= 0 ? 'delayed' : 'ahead' - }`; + /** + * @description handles direction change to delay + * @param newDirection + */ + const handleSlipChange = (newDirection: 'add' | 'subtract') => { + if (newDirection === 'add') { + // add time + if (duration < 0) { + submitChange(duration * -1); + } + } else if (newDirection === 'subtract') { + // subtract time + if (duration > 0) { + submitChange(duration * -1); + } + } + }; + + const checkedOption = value.startsWith('-') ? 'subtract' : 'add'; return ( -