mirror of
https://github.com/cpvalente/ontime.git
synced 2026-09-01 04:19:12 +00:00
feat: option to select all events for countdown
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
import { useMemo, useState } from 'react';
|
import { useMemo, useState } from 'react';
|
||||||
import { IoAdd } from 'react-icons/io5';
|
import { IoAdd } from 'react-icons/io5';
|
||||||
import { EntryId, isOntimeEvent, isPlayableEvent, OntimeEvent, OntimeView } from 'ontime-types';
|
import { isOntimeEvent, isPlayableEvent, OntimeEvent, OntimeView } from 'ontime-types';
|
||||||
|
|
||||||
import Button from '../../common/components/buttons/Button';
|
import Button from '../../common/components/buttons/Button';
|
||||||
import Empty from '../../common/components/state/Empty';
|
import Empty from '../../common/components/state/Empty';
|
||||||
@@ -16,7 +16,7 @@ import { useTranslation } from '../../translation/TranslationProvider';
|
|||||||
import Loader from '../common/loader/Loader';
|
import Loader from '../common/loader/Loader';
|
||||||
|
|
||||||
import { getCountdownOptions, useCountdownOptions } from './countdown.options';
|
import { getCountdownOptions, useCountdownOptions } from './countdown.options';
|
||||||
import { getOrderedSubscriptions } from './countdown.utils';
|
import { CountdownSubscription, getOrderedSubscriptions } from './countdown.utils';
|
||||||
import CountdownSelect from './CountdownSelect';
|
import CountdownSelect from './CountdownSelect';
|
||||||
import CountdownSubscriptions from './CountdownSubscriptions';
|
import CountdownSubscriptions from './CountdownSubscriptions';
|
||||||
import SingleEventCountdown from './SingleEventCountdown';
|
import SingleEventCountdown from './SingleEventCountdown';
|
||||||
@@ -59,6 +59,8 @@ function Countdown({ customFields, rundownData, projectData, isMirrored, setting
|
|||||||
[defaultFormat, customFields, subscriptions],
|
[defaultFormat, customFields, subscriptions],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
console.log(subscriptions)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={`countdown ${isMirrored ? 'mirror' : ''}`} data-testid='countdown-view'>
|
<div className={`countdown ${isMirrored ? 'mirror' : ''}`} data-testid='countdown-view'>
|
||||||
<ViewParamsEditor target={OntimeView.Countdown} viewOptions={countdownOptions} />
|
<ViewParamsEditor target={OntimeView.Countdown} viewOptions={countdownOptions} />
|
||||||
@@ -87,7 +89,7 @@ function Countdown({ customFields, rundownData, projectData, isMirrored, setting
|
|||||||
|
|
||||||
interface CountdownContentsProps {
|
interface CountdownContentsProps {
|
||||||
playableEvents: ExtendedEntry<OntimeEvent>[];
|
playableEvents: ExtendedEntry<OntimeEvent>[];
|
||||||
subscriptions: EntryId[];
|
subscriptions: CountdownSubscription;
|
||||||
goToEditMode: () => void;
|
goToEditMode: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { IoArrowBack, IoClose, IoSaveOutline } from 'react-icons/io5';
|
import { IoArrowBack, IoClose, IoSaveOutline, IoAlbumsOutline } from 'react-icons/io5';
|
||||||
import { useNavigate } from 'react-router';
|
import { useNavigate } from 'react-router';
|
||||||
import { EntryId, OntimeEvent } from 'ontime-types';
|
import { EntryId, OntimeEvent } from 'ontime-types';
|
||||||
|
|
||||||
@@ -7,18 +7,19 @@ import Button from '../../common/components/buttons/Button';
|
|||||||
import { cx } from '../../common/utils/styleUtils';
|
import { cx } from '../../common/utils/styleUtils';
|
||||||
import ClockTime from '../../features/viewers/common/clock-time/ClockTime';
|
import ClockTime from '../../features/viewers/common/clock-time/ClockTime';
|
||||||
|
|
||||||
import { makeSubscriptionsUrl } from './countdown.utils';
|
import { CountdownSubscription, makeSubscriptionsUrl } from './countdown.utils';
|
||||||
|
|
||||||
import './Countdown.scss';
|
import './Countdown.scss';
|
||||||
|
|
||||||
interface CountdownSelectProps {
|
interface CountdownSelectProps {
|
||||||
events: OntimeEvent[];
|
events: OntimeEvent[];
|
||||||
subscriptions: EntryId[];
|
subscriptions: CountdownSubscription;
|
||||||
disableEdit: () => void;
|
disableEdit: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function CountdownSelect({ events, subscriptions, disableEdit }: CountdownSelectProps) {
|
export default function CountdownSelect({ events, subscriptions, disableEdit }: CountdownSelectProps) {
|
||||||
const [selected, setSelected] = useState<EntryId[]>(subscriptions);
|
const maybeAllSubscriptions: EntryId[] = subscriptions === 'all' ? events.map((event) => event.id) : subscriptions;
|
||||||
|
const [selected, setSelected] = useState<EntryId[]>(maybeAllSubscriptions);
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -47,6 +48,18 @@ export default function CountdownSelect({ events, subscriptions, disableEdit }:
|
|||||||
navigate(url.search.toString());
|
navigate(url.search.toString());
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a URL with all
|
||||||
|
* and navigates to it
|
||||||
|
*/
|
||||||
|
const applyAll = () => {
|
||||||
|
// we remove events that no longer exist to avoid stale subscriptions
|
||||||
|
const url = makeSubscriptionsUrl(window.location.href, 'all');
|
||||||
|
disableEdit();
|
||||||
|
setSelected([]);
|
||||||
|
navigate(url.search.toString());
|
||||||
|
};
|
||||||
|
|
||||||
// make a copy of the selected array for quick lookup
|
// make a copy of the selected array for quick lookup
|
||||||
const selectedIds = new Set(selected);
|
const selectedIds = new Set(selected);
|
||||||
|
|
||||||
@@ -86,6 +99,10 @@ export default function CountdownSelect({ events, subscriptions, disableEdit }:
|
|||||||
<Button variant='subtle' size='xlarge' onClick={disableEdit}>
|
<Button variant='subtle' size='xlarge' onClick={disableEdit}>
|
||||||
<IoArrowBack /> Go back
|
<IoArrowBack /> Go back
|
||||||
</Button>
|
</Button>
|
||||||
|
<Button variant='subtle' size='xlarge' onClick={applyAll}>
|
||||||
|
{/* TODO: icon ??? */}
|
||||||
|
<IoAlbumsOutline /> Use All
|
||||||
|
</Button>
|
||||||
<Button variant='subtle' size='xlarge' onClick={() => setSelected([])} disabled={selected.length === 0}>
|
<Button variant='subtle' size='xlarge' onClick={() => setSelected([])} disabled={selected.length === 0}>
|
||||||
<IoClose /> Clear
|
<IoClose /> Clear
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -8,11 +8,12 @@ import { ViewOption } from '../../common/components/view-params-editor/viewParam
|
|||||||
import { makeOptionsFromCustomFields } from '../../common/components/view-params-editor/viewParams.utils';
|
import { makeOptionsFromCustomFields } from '../../common/components/view-params-editor/viewParams.utils';
|
||||||
import { PresetContext } from '../../common/context/PresetContext';
|
import { PresetContext } from '../../common/context/PresetContext';
|
||||||
import { isStringBoolean } from '../../features/viewers/common/viewUtils';
|
import { isStringBoolean } from '../../features/viewers/common/viewUtils';
|
||||||
|
import { CountdownSubscription } from './countdown.utils';
|
||||||
|
|
||||||
export const getCountdownOptions = (
|
export const getCountdownOptions = (
|
||||||
timeFormat: string,
|
timeFormat: string,
|
||||||
customFields: CustomFields,
|
customFields: CustomFields,
|
||||||
persistedSubscriptions: EntryId[],
|
persistedSubscriptions: CountdownSubscription,
|
||||||
): ViewOption[] => {
|
): ViewOption[] => {
|
||||||
const secondaryOptions = makeOptionsFromCustomFields(customFields, [
|
const secondaryOptions = makeOptionsFromCustomFields(customFields, [
|
||||||
{ value: 'none', label: 'None' },
|
{ value: 'none', label: 'None' },
|
||||||
@@ -55,7 +56,7 @@ export const getCountdownOptions = (
|
|||||||
id: 'sub',
|
id: 'sub',
|
||||||
title: 'Event subscription',
|
title: 'Event subscription',
|
||||||
description: 'The events to follow',
|
description: 'The events to follow',
|
||||||
values: persistedSubscriptions,
|
values: persistedSubscriptions === 'all' ? ['all'] : persistedSubscriptions,
|
||||||
type: 'persist',
|
type: 'persist',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@@ -64,7 +65,7 @@ export const getCountdownOptions = (
|
|||||||
};
|
};
|
||||||
|
|
||||||
type CountdownOptions = {
|
type CountdownOptions = {
|
||||||
subscriptions: EntryId[];
|
subscriptions: CountdownSubscription;
|
||||||
secondarySource: keyof OntimeEvent | null;
|
secondarySource: keyof OntimeEvent | null;
|
||||||
showExpected: boolean;
|
showExpected: boolean;
|
||||||
};
|
};
|
||||||
@@ -85,8 +86,10 @@ function getOptionsFromParams(searchParams: URLSearchParams, defaultValues?: URL
|
|||||||
return searchParams.getAll(key) as EntryId[];
|
return searchParams.getAll(key) as EntryId[];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const subscriptions = getArrayValues('sub');
|
||||||
|
|
||||||
return {
|
return {
|
||||||
subscriptions: getArrayValues('sub'),
|
subscriptions: subscriptions.at(0) === 'all' ? 'all' : subscriptions,
|
||||||
secondarySource: getValue('secondary-src') as keyof OntimeEvent | null,
|
secondarySource: getValue('secondary-src') as keyof OntimeEvent | null,
|
||||||
showExpected: isStringBoolean(getValue('showExpected')),
|
showExpected: isStringBoolean(getValue('showExpected')),
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -14,6 +14,8 @@ export function sanitiseTitle(title: string | null) {
|
|||||||
return title ?? '{no title}';
|
return title ?? '{no title}';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type CountdownSubscription = EntryId[] | 'all';
|
||||||
|
|
||||||
export const preferredFormat12 = 'h:mm a';
|
export const preferredFormat12 = 'h:mm a';
|
||||||
export const preferredFormat24 = 'HH:mm';
|
export const preferredFormat24 = 'HH:mm';
|
||||||
|
|
||||||
@@ -120,7 +122,7 @@ export function useSubscriptionDisplayData(
|
|||||||
/**
|
/**
|
||||||
* Adds a set of subscriptions to the URL parameters
|
* Adds a set of subscriptions to the URL parameters
|
||||||
*/
|
*/
|
||||||
export function makeSubscriptionsUrl(urlRef: string, subscriptions: EntryId[]) {
|
export function makeSubscriptionsUrl(urlRef: string, subscriptions: CountdownSubscription) {
|
||||||
const url = new URL(urlRef);
|
const url = new URL(urlRef);
|
||||||
const newParams = new URLSearchParams();
|
const newParams = new URLSearchParams();
|
||||||
|
|
||||||
@@ -131,10 +133,14 @@ export function makeSubscriptionsUrl(urlRef: string, subscriptions: EntryId[]) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// add new subscriptions
|
if (subscriptions === 'all') {
|
||||||
subscriptions.forEach((id) => {
|
newParams.append('sub', 'all');
|
||||||
newParams.append('sub', id);
|
} else {
|
||||||
});
|
// add new subscriptions
|
||||||
|
subscriptions.forEach((id) => {
|
||||||
|
newParams.append('sub', id);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
url.search = newParams.toString();
|
url.search = newParams.toString();
|
||||||
|
|
||||||
@@ -146,7 +152,11 @@ export function makeSubscriptionsUrl(urlRef: string, subscriptions: EntryId[]) {
|
|||||||
* Since the original array is already ordered, we simply filter out the events
|
* Since the original array is already ordered, we simply filter out the events
|
||||||
* which are not in the subscriptions list.
|
* which are not in the subscriptions list.
|
||||||
*/
|
*/
|
||||||
export function getOrderedSubscriptions<T extends OntimeEntry>(subscriptions: EntryId[], playableEvents: T[]): T[] {
|
export function getOrderedSubscriptions<T extends OntimeEntry>(
|
||||||
|
subscriptions: CountdownSubscription,
|
||||||
|
playableEvents: T[],
|
||||||
|
): T[] {
|
||||||
|
if (subscriptions === 'all') return playableEvents;
|
||||||
return playableEvents.filter((event) => subscriptions.includes(event.id));
|
return playableEvents.filter((event) => subscriptions.includes(event.id));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user