mirror of
https://github.com/cpvalente/ontime.git
synced 2026-07-26 10:38:55 +00:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e6e1c38355 |
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@getontime/cli",
|
||||
"version": "3.14.5",
|
||||
"version": "3.14.3",
|
||||
"author": "Carlos Valente",
|
||||
"description": "Time keeping for live events",
|
||||
"repository": "https://github.com/cpvalente/ontime",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "ontime-ui",
|
||||
"version": "3.14.5",
|
||||
"version": "3.14.3",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
|
||||
@@ -4,10 +4,10 @@ import type { MultiselectOptions, ParamField } from './types';
|
||||
|
||||
export const makeOptionsFromCustomFields = (
|
||||
customFields: CustomFields,
|
||||
additionalOptions: Readonly<Record<string, string>> = {},
|
||||
additionalOptions: Record<string, string> = {},
|
||||
filterImageType = true,
|
||||
) => {
|
||||
const options = { ...additionalOptions };
|
||||
const options = structuredClone(additionalOptions);
|
||||
for (const [key, value] of Object.entries(customFields)) {
|
||||
if (filterImageType && value.type === 'image') {
|
||||
continue;
|
||||
|
||||
@@ -48,12 +48,10 @@ export const connectSocket = () => {
|
||||
|
||||
websocket.onclose = () => {
|
||||
console.warn('WebSocket disconnected');
|
||||
setOnlineStatus(false);
|
||||
|
||||
if (shouldReconnect) {
|
||||
reconnectTimeout = setTimeout(() => {
|
||||
if (reconnectAttempts > 2) {
|
||||
setOnlineStatus(false);
|
||||
}
|
||||
console.warn('WebSocket: attempting reconnect');
|
||||
if (websocket && websocket.readyState === WebSocket.CLOSED) {
|
||||
reconnectAttempts += 1;
|
||||
|
||||
+1
-1
@@ -94,7 +94,7 @@ export default function ReportSettings() {
|
||||
})();
|
||||
return (
|
||||
<tr key={entry.index}>
|
||||
<th>{entry.index}</th>
|
||||
<th>{entry.index + 1}</th>
|
||||
<th>{entry.cue}</th>
|
||||
<th>{entry.title}</th>
|
||||
<th className={cx([start && style[start]])}>{formatTime(entry.scheduledStart)}</th>
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import { PropsWithChildren, useEffect, useRef, useState } from 'react';
|
||||
import { useEffect, useRef } from 'react';
|
||||
import { IoEye, IoEyeOffOutline } from 'react-icons/io5';
|
||||
import { Input } from '@chakra-ui/react';
|
||||
|
||||
import { cx } from '../../../common/utils/styleUtils';
|
||||
import TooltipActionBtn from '../../../common/components/buttons/TooltipActionBtn';
|
||||
import { tooltipDelayMid } from '../../../ontimeConfig';
|
||||
|
||||
import style from './InputRow.module.scss';
|
||||
|
||||
@@ -10,53 +12,50 @@ interface InputRowProps {
|
||||
placeholder: string;
|
||||
text: string;
|
||||
visible: boolean;
|
||||
actionHandler: () => void;
|
||||
changeHandler: (newValue: string) => void;
|
||||
}
|
||||
|
||||
export default function InputRow(props: PropsWithChildren<InputRowProps>) {
|
||||
const { label, placeholder, text, visible, changeHandler, children } = props;
|
||||
export default function InputRow(props: InputRowProps) {
|
||||
const { label, placeholder, text, visible, actionHandler, changeHandler } = props;
|
||||
|
||||
const [value, setValue] = useState(text);
|
||||
const inputRef = useRef<HTMLInputElement>(null);
|
||||
const cursorPositionRef = useRef(0);
|
||||
|
||||
// sync cursor position with text
|
||||
useEffect(() => {
|
||||
if (inputRef.current && inputRef.current !== document.activeElement) {
|
||||
if (inputRef.current) {
|
||||
inputRef.current.selectionStart = cursorPositionRef.current;
|
||||
inputRef.current.selectionEnd = cursorPositionRef.current;
|
||||
}
|
||||
}, [text]);
|
||||
|
||||
// synchronise external text
|
||||
useEffect(() => {
|
||||
if (inputRef.current !== document.activeElement) {
|
||||
setValue(text);
|
||||
}
|
||||
}, [text]);
|
||||
|
||||
const handleInputChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
cursorPositionRef.current = event.target.selectionStart ?? 0;
|
||||
setValue(event.target.value);
|
||||
changeHandler(event.target.value);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={style.inputRow}>
|
||||
<label className={cx([style.label, visible ?? style.active])} htmlFor={label}>
|
||||
{label}
|
||||
</label>
|
||||
<label className={`${style.label} ${visible ? style.active : ''}`}>{label}</label>
|
||||
<div className={style.inputItems}>
|
||||
<Input
|
||||
id={label}
|
||||
ref={inputRef}
|
||||
size='sm'
|
||||
variant='ontime-filled'
|
||||
value={value}
|
||||
value={text}
|
||||
onChange={handleInputChange}
|
||||
placeholder={placeholder}
|
||||
/>
|
||||
{children}
|
||||
<TooltipActionBtn
|
||||
clickHandler={actionHandler}
|
||||
tooltip={visible ? 'Make invisible' : 'Make visible'}
|
||||
aria-label={`Toggle ${label}`}
|
||||
openDelay={tooltipDelayMid}
|
||||
icon={visible ? <IoEye size='18px' /> : <IoEyeOffOutline size='18px' />}
|
||||
variant={visible ? 'ontime-filled' : 'ontime-subtle'}
|
||||
size='sm'
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
import { IoEye, IoEyeOffOutline } from 'react-icons/io5';
|
||||
|
||||
import TooltipActionBtn from '../../../common/components/buttons/TooltipActionBtn';
|
||||
import { setMessage, useExternalMessageInput, useTimerMessageInput } from '../../../common/hooks/useSocket';
|
||||
import { tooltipDelayMid } from '../../../ontimeConfig';
|
||||
|
||||
import InputRow from './InputRow';
|
||||
import TimerControlsPreview from './TimerViewControl';
|
||||
@@ -27,17 +23,8 @@ function TimerMessageInput() {
|
||||
text={text}
|
||||
visible={visible}
|
||||
changeHandler={(newValue) => setMessage.timerText(newValue)}
|
||||
>
|
||||
<TooltipActionBtn
|
||||
clickHandler={() => setMessage.timerVisible(!visible)}
|
||||
tooltip={visible ? 'Make invisible' : 'Make visible'}
|
||||
aria-label='Toggle timer message visibility'
|
||||
openDelay={tooltipDelayMid}
|
||||
icon={visible ? <IoEye size='18px' /> : <IoEyeOffOutline size='18px' />}
|
||||
variant={visible ? 'ontime-filled' : 'ontime-subtle'}
|
||||
size='sm'
|
||||
/>
|
||||
</InputRow>
|
||||
actionHandler={() => setMessage.timerVisible(!visible)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -59,16 +46,7 @@ function ExternalInput() {
|
||||
text={text}
|
||||
visible={visible}
|
||||
changeHandler={(newValue) => setMessage.externalText(newValue)}
|
||||
>
|
||||
<TooltipActionBtn
|
||||
clickHandler={toggleExternal}
|
||||
tooltip={visible ? 'Make invisible' : 'Make visible'}
|
||||
aria-label='Toggle external message visibility'
|
||||
openDelay={tooltipDelayMid}
|
||||
icon={visible ? <IoEye size='18px' /> : <IoEyeOffOutline size='18px' />}
|
||||
variant={visible ? 'ontime-filled' : 'ontime-subtle'}
|
||||
size='sm'
|
||||
/>
|
||||
</InputRow>
|
||||
actionHandler={toggleExternal}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -300,8 +300,7 @@ export default function Rundown({ data }: RundownProps) {
|
||||
isNextDay = checkIsNextDay(entry, lastEvent);
|
||||
if (!isPast) {
|
||||
totalGap += entry.gap;
|
||||
// We also include countToEnd in this test as the behavior of a linked event coming after a countToEnd is simelar to an unlinked event
|
||||
isLinkedToLoaded = isLinkedToLoaded && entry.linkStart !== null && !lastEvent?.countToEnd;
|
||||
isLinkedToLoaded = isLinkedToLoaded && entry.linkStart !== null;
|
||||
}
|
||||
if (isNewLatest(entry, lastEvent)) {
|
||||
// populate previous entry
|
||||
|
||||
@@ -140,7 +140,7 @@ export const ScheduleProvider = ({
|
||||
let selectedEventIndex = events.findIndex((event) => event.id === selectedEventId);
|
||||
|
||||
// we want to show the event after the current
|
||||
const viewEvents = events.slice(selectedEventIndex + 1);
|
||||
const viewEvents = events.toSpliced(0, selectedEventIndex + 1);
|
||||
selectedEventIndex = 0;
|
||||
|
||||
return (
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "ontime-electron",
|
||||
"version": "3.14.5",
|
||||
"version": "3.14.3",
|
||||
"author": "Carlos Valente",
|
||||
"description": "Time keeping for live events",
|
||||
"repository": "https://github.com/cpvalente/ontime",
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"name": "ontime-server",
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
"version": "3.14.5",
|
||||
"version": "3.14.3",
|
||||
"exports": "./src/index.js",
|
||||
"dependencies": {
|
||||
"@googleapis/sheets": "^5.0.5",
|
||||
|
||||
@@ -124,6 +124,6 @@ function setSessionCookie(res: Response, token: string) {
|
||||
httpOnly: false, // allow websocket to access cookie
|
||||
secure: true,
|
||||
path: '/', // allow cookie to be accessed from any path
|
||||
sameSite: 'none', // allow cookies to be sent in cross-origin requests (e.g., iframes)
|
||||
sameSite: 'strict',
|
||||
});
|
||||
}
|
||||
|
||||
@@ -108,7 +108,7 @@ export async function deleteEvent(eventIds: string[]) {
|
||||
const scopedMutation = cache.mutateCache(cache.remove);
|
||||
const { didMutate } = await scopedMutation({ eventIds });
|
||||
|
||||
if (!didMutate) {
|
||||
if (didMutate === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -146,7 +146,7 @@ export async function editEvent(patch: PatchWithId) {
|
||||
const { newEvent, didMutate } = await scopedMutation({ patch, eventId: patch.id });
|
||||
|
||||
// short circuit if nothing changed
|
||||
if (!didMutate) {
|
||||
if (didMutate === false) {
|
||||
return newEvent;
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import type { Server } from 'http';
|
||||
import { networkInterfaces } from 'os';
|
||||
import type { AddressInfo } from 'net';
|
||||
|
||||
import { isDocker, isOntimeCloud, isProduction } from '../externals.js';
|
||||
import { isDocker, isProduction } from '../externals.js';
|
||||
import { logger } from '../classes/Logger.js';
|
||||
|
||||
/**
|
||||
@@ -42,10 +42,6 @@ export function getNetworkInterfaces(): { name: string; address: string }[] {
|
||||
* @throws any other server errors will result in a throw
|
||||
*/
|
||||
export function serverTryDesiredPort(server: Server, desiredPort: number): Promise<number> {
|
||||
if (isOntimeCloud) {
|
||||
return forceCloudPort(server);
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
server.once('error', (error) => {
|
||||
// we should only move ports if we are in a desktop environment
|
||||
@@ -88,19 +84,6 @@ export function serverTryDesiredPort(server: Server, desiredPort: number): Promi
|
||||
});
|
||||
}
|
||||
|
||||
function forceCloudPort(server: Server): Promise<number> {
|
||||
return new Promise((resolve, reject) => {
|
||||
server.listen(4001, '0.0.0.0', () => {
|
||||
const address = server.address();
|
||||
if (!isAddressInfo(address)) {
|
||||
reject(new Error('Unknown port type, unable to proceed'));
|
||||
return;
|
||||
}
|
||||
resolve(address.port);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Guard verifies that the given address is a usable AddressInfo object
|
||||
*/
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "ontime",
|
||||
"version": "3.14.5",
|
||||
"version": "3.14.3",
|
||||
"description": "Time keeping for live events",
|
||||
"keywords": [
|
||||
"ontime",
|
||||
|
||||
@@ -31,7 +31,7 @@ export function insertAtIndex<T>(index: number, item: T, array: T[]): T[] {
|
||||
* @param array
|
||||
*/
|
||||
export function deleteAtIndex<T>(index: number, array: T[]) {
|
||||
return array.filter((_, i) => i !== index);
|
||||
return array.toSpliced(index, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user