Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: add options to hide schedule #1203

Merged
merged 1 commit into from
Sep 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions apps/client/src/features/viewers/studio/StudioClock.scss
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,11 @@ $orange-active: #f60;
grid-template-areas: 'clock schedule';
text-transform: uppercase;

&.hide-right {
grid-template-columns: 1fr;
grid-template-areas: 'clock';
}

.clock-container {
grid-area: clock;
display: grid;
Expand Down
58 changes: 17 additions & 41 deletions apps/client/src/features/viewers/studio/StudioClock.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { useSearchParams } from 'react-router-dom';
import type { OntimeEvent, OntimeRundown, Settings, ViewSettings } from 'ontime-types';
import { isOntimeEvent, Playback } from 'ontime-types';
import { millisToString, removeSeconds } from 'ontime-utils';
import type { MaybeString, OntimeEvent, OntimeRundown, Settings, ViewSettings } from 'ontime-types';
import { Playback } from 'ontime-types';
import { millisToString, removeSeconds, secondsInMillis } from 'ontime-utils';

import { overrideStylesURL } from '../../../common/api/constants';
import ViewParamsEditor from '../../../common/components/view-params-editor/ViewParamsEditor';
Expand All @@ -10,11 +10,10 @@ import { useRuntimeStylesheet } from '../../../common/hooks/useRuntimeStylesheet
import { useWindowTitle } from '../../../common/hooks/useWindowTitle';
import { ViewExtendedTimer } from '../../../common/models/TimeManager.type';
import { formatTime, getDefaultFormat } from '../../../common/utils/time';
import SuperscriptTime from '../common/superscript-time/SuperscriptTime';
import { isStringBoolean } from '../common/viewUtils';

import { getStudioClockOptions } from './studioClock.options';
import { secondsInMillis, trimRundown } from './studioClock.utils';
import StudioClockSchedule from './StudioClockSchedule';

import './StudioClock.scss';

Expand All @@ -23,8 +22,8 @@ interface StudioClockProps {
eventNext: OntimeEvent | null;
time: ViewExtendedTimer;
backstageEvents: OntimeRundown;
selectedId: string | null;
nextId: string | null;
selectedId: MaybeString;
nextId: MaybeString;
onAir: boolean;
viewSettings: ViewSettings;
settings: Settings | undefined;
Expand All @@ -38,16 +37,13 @@ export default function StudioClock(props: StudioClockProps) {
useRuntimeStylesheet(viewSettings?.overrideStyles && overrideStylesURL);
const { fontSize: titleFontSize, ref: titleRef } = useFitText({ minFontSize: 150, maxFontSize: 500 });

const activeIndicators = [...Array(12).keys()];
const secondsIndicators = [...Array(60).keys()];

// TODO: fit titles on screen
const MAX_TITLES = 11;

const [searchParams] = useSearchParams();

useWindowTitle('Studio Clock');

const activeIndicators = [...Array(12).keys()];
const secondsIndicators = [...Array(60).keys()];

let clock = formatTime(time.clock);
let hasAmPm = '';
if (clock.includes('AM')) {
Expand All @@ -65,16 +61,18 @@ export default function StudioClock(props: StudioClockProps) {
const defaultFormat = getDefaultFormat(settings?.timeFormat);
const studioClockOptions = getStudioClockOptions(defaultFormat);

const delayed = backstageEvents.filter((event) => isOntimeEvent(event)) as OntimeEvent[];
const trimmedRundown = trimRundown(delayed, selectedId, MAX_TITLES);
const hideRight = isStringBoolean(searchParams.get('hideRight'));
let timer = millisToString(time.current, { fallback: '---' });
const hideSeconds = isStringBoolean(searchParams.get('hideTimerSeconds'));
if (time.current != null && hideSeconds) {
timer = removeSeconds(timer);
}

return (
<div className={`studio-clock ${isMirrored ? 'mirror' : ''}`} data-testid='studio-view'>
<div
className={`studio-clock ${isMirrored ? 'mirror' : ''} ${hideRight ? 'hide-right' : ''}`}
data-testid='studio-view'
>
<ViewParamsEditor viewOptions={studioClockOptions} />
<div className='clock-container'>
{hasAmPm && <div className='clock__ampm'>{hasAmPm}</div>}
Expand Down Expand Up @@ -114,31 +112,9 @@ export default function StudioClock(props: StudioClockProps) {
))}
</div>
</div>
<div className='schedule-container'>
<div
className={onAir ? 'onAir' : 'onAir onAir--idle'}
data-testid={onAir ? 'on-air-enabled' : 'on-air-disabled'}
>
ON AIR
</div>
<ul className='schedule'>
{trimmedRundown.map((event) => {
const start = formatTime(event.timeStart + (event?.delay ?? 0), { format12: 'h:mm a', format24: 'HH:mm' });
const isSelected = event.id === selectedId;
const isNext = event.id === nextId;
const classes = `schedule__item schedule__item${isSelected ? '--now' : isNext ? '--next' : '--future'}`;
return (
<li key={event.id} className={classes}>
<span className='event'>
<span className='event__colour' style={{ backgroundColor: `${event.colour}` }} />
<SuperscriptTime time={start} />
</span>
<span>{event.title}</span>
</li>
);
})}
</ul>
</div>
{!hideRight && (
<StudioClockSchedule rundown={backstageEvents} selectedId={selectedId} nextId={nextId} onAir={onAir} />
)}
</div>
);
}
50 changes: 50 additions & 0 deletions apps/client/src/features/viewers/studio/StudioClockSchedule.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { isOntimeEvent, MaybeString, OntimeEvent, OntimeRundown } from 'ontime-types';

import { formatTime } from '../../../common/utils/time';
import SuperscriptTime from '../common/superscript-time/SuperscriptTime';

import { trimRundown } from './studioClock.utils';

import './StudioClock.scss';

interface StudioClockScheduleProps {
rundown: OntimeRundown;
selectedId: MaybeString;
nextId: MaybeString;
onAir: boolean;
}

// TODO: fit titles on screen
const MAX_TITLES = 11;

export default function StudioClockSchedule(props: StudioClockScheduleProps) {
const { rundown, selectedId, nextId, onAir } = props;

const delayed = rundown.filter((event) => isOntimeEvent(event)) as OntimeEvent[];
const trimmedRundown = trimRundown(delayed, selectedId, MAX_TITLES);

return (
<div className='schedule-container'>
<div className={onAir ? 'onAir' : 'onAir onAir--idle'} data-testid={onAir ? 'on-air-enabled' : 'on-air-disabled'}>
ON AIR
</div>
<ul className='schedule'>
{trimmedRundown.map((event) => {
const start = formatTime(event.timeStart + (event?.delay ?? 0), { format12: 'h:mm a', format24: 'HH:mm' });
const isSelected = event.id === selectedId;
const isNext = event.id === nextId;
const classes = `schedule__item schedule__item${isSelected ? '--now' : isNext ? '--next' : '--future'}`;
return (
<li key={event.id} className={classes}>
<span className='event'>
<span className='event__colour' style={{ backgroundColor: `${event.colour}` }} />
<SuperscriptTime time={start} />
</span>
<span>{event.title}</span>
</li>
);
})}
</ul>
</div>
);
}
Comment on lines +20 to +50
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Component logic and JSX structure review.

The component logic is mostly clear and functional. However, there are a few areas that could be improved:

  1. Conditional Class Application: The way CSS classes are applied based on the onAir prop could be simplified for better readability. Consider using a utility function to handle class names based on conditions.
  2. Event Filtering: The filtering of events to check if they are OntimeEvent instances is crucial. Ensure that this logic is robust and consider adding error handling if the data structure is not as expected.
  3. TODO Comment: The TODO comment about fitting titles on the screen suggests that there might be UI issues with long titles. It would be beneficial to address this or at least track it in your project management tool.

Overall, the component does what it is supposed to do but consider refining these areas to enhance maintainability and readability.

Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { OntimeEvent } from 'ontime-types';

import { secondsInMillis, trimRundown } from '../studioClock.utils';
import { trimRundown } from '../studioClock.utils';

describe('test trimEventlist function', () => {
const limit = 8;
Expand Down Expand Up @@ -118,14 +118,3 @@ describe('test trimEventlist function', () => {
expect(l).toStrictEqual(expected);
});
});

describe('secondsInMillis()', () => {
it('return 0 if value is null', () => {
expect(secondsInMillis(null)).toBe(0);
});
it('returns the seconds value of a millis date', () => {
const date = 1686255053619; // Thu Jun 08 2023 20:10:53
const seconds = secondsInMillis(date);
expect(seconds).toBe(53);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,12 @@ export const getStudioClockOptions = (timeFormat: string): ViewOption[] => [
getTimeOption(timeFormat),
{ section: 'Timer Options' },
hideTimerSeconds,
{ section: 'Element visibility' },
{
id: 'hideRight',
title: 'Hide right section',
description: 'Hides the right section with On Air indicator and the schedule',
type: 'boolean',
defaultValue: false,
},
];
15 changes: 1 addition & 14 deletions apps/client/src/features/viewers/studio/studioClock.utils.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { MaybeNumber, OntimeEvent } from 'ontime-types';
import { MILLIS_PER_MINUTE, MILLIS_PER_SECOND } from 'ontime-utils';
import { OntimeEvent } from 'ontime-types';

/**
* @description Returns trimmed event list array
Expand All @@ -20,15 +19,3 @@ export function trimRundown(rundown: OntimeEvent[], selectedId: string | null, l
const trimmedRundown = rundown.slice(startIndex, endIndex);
return trimmedRundown;
}

/**
* @description Returns amount of seconds in a date given in milliseconds. For studio clock second indicator
* @param {MaybeNumber} millis time to format
* @returns amount of elapsed seconds
*/
export function secondsInMillis(millis: MaybeNumber): number {
if (!millis) {
return 0;
}
return Math.floor((millis % MILLIS_PER_MINUTE) / MILLIS_PER_SECOND);
}
1 change: 1 addition & 0 deletions packages/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ export {
millisToHours,
millisToMinutes,
millisToSeconds,
secondsInMillis,
} from './src/date-utils/conversionUtils.js';
export { isTimeString } from './src/date-utils/isTimeString.js';
export {
Expand Down
13 changes: 12 additions & 1 deletion packages/utils/src/date-utils/conversionUtils.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { millisToHours, millisToMinutes, millisToSeconds } from "./conversionUtils";
import { millisToHours, millisToMinutes, millisToSeconds, secondsInMillis } from './conversionUtils';

describe('millisToSecond()', () => {
test('null values', () => {
Expand Down Expand Up @@ -112,3 +112,14 @@ describe('millisToHours()', () => {
expect(millisToHours(t.val)).toBe(t.result);
});
});

describe('secondsInMillis()', () => {
it('return 0 if value is null', () => {
expect(secondsInMillis(null)).toBe(0);
});
it('returns the seconds value of a millis date', () => {
const date = 1686255053619; // Thu Jun 08 2023 20:10:53
const seconds = secondsInMillis(date);
expect(seconds).toBe(53);
});
});
12 changes: 12 additions & 0 deletions packages/utils/src/date-utils/conversionUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,3 +64,15 @@ export function secondsToMinutes(seconds: number): number {
export function secondsToHours(seconds: number): number {
return Math.floor(seconds / 3600);
}

/**
* @description Returns amount of seconds in a date given in milliseconds. For studio clock second indicator
* @param {MaybeNumber} millis time to format
* @returns amount of elapsed seconds
*/
export function secondsInMillis(millis: MaybeNumber): number {
if (!millis) {
return 0;
}
return Math.floor((millis % MILLIS_PER_MINUTE) / MILLIS_PER_SECOND);
}