v2 beta 5 (#375)

* chore: update documentation links

* fix: sentry has no access to error context

* style: clarify event history

* fix: issue with clipboard write in safari

* style: clarify event history

* refactor: improvements to follow logic in rundown

* refactor: improvements in go mode

* several style tweaks and small improvements
This commit is contained in:
Carlos Valente
2023-05-12 22:31:26 +02:00
committed by GitHub
parent 139b667e20
commit 305d6b6476
19 changed files with 122 additions and 92 deletions
@@ -14,21 +14,18 @@ interface CopyTagProps {
export default function CopyTag(props: PropsWithChildren<CopyTagProps>) { export default function CopyTag(props: PropsWithChildren<CopyTagProps>) {
const { label, className, size = 'xs', children } = props; const { label, className, size = 'xs', children } = props;
const handleClick = () => {
// we need to this as a promise because safari
setTimeout(async () => await navigator.clipboard.writeText(children as string));
};
return ( return (
<Tooltip label={label} openDelay={tooltipDelayFast}> <Tooltip label={label} openDelay={tooltipDelayFast}>
<ButtonGroup <ButtonGroup size={size} isAttached className={className}>
size={size} <Button variant='ontime-subtle' tabIndex={-1}>
isAttached {children}
className={className} </Button>
> <IconButton aria-label={label} icon={<IoCopy />} variant='ontime-filled' tabIndex={-1} onClick={handleClick} />
<Button variant='ontime-subtle' tabIndex={-1}>{children}</Button>
<IconButton
aria-label={label}
icon={<IoCopy />}
variant='ontime-filled'
tabIndex={-1}
onClick={() => navigator.clipboard.writeText(children as string)}
/>
</ButtonGroup> </ButtonGroup>
</Tooltip> </Tooltip>
); );
@@ -28,12 +28,6 @@ class ErrorBoundary extends React.Component {
const eventId = Sentry.captureException(error); const eventId = Sentry.captureException(error);
this.setState({ eventId, info }); this.setState({ eventId, info });
}); });
try {
this.context.emitError(error.toString());
} catch (e) {
Sentry.captureMessage(`Unable to emit error ${error} ${e}`);
}
this.reportContent = `${error} ${info.componentStack}`; this.reportContent = `${error} ${info.componentStack}`;
} }
+12 -1
View File
@@ -5,6 +5,16 @@ export enum AppMode {
Edit = 'edit', Edit = 'edit',
} }
const appModeKey = 'ontime-app-mode';
function getModeFromSession() {
return localStorage.getItem(appModeKey) === AppMode.Run ? AppMode.Run : AppMode.Edit;
}
async function persistModeToSession(mode: AppMode) {
localStorage.setItem(appModeKey, mode);
}
type AppModeStore = { type AppModeStore = {
mode: AppMode; mode: AppMode;
cursor: string | null; cursor: string | null;
@@ -15,11 +25,12 @@ type AppModeStore = {
}; };
export const useAppMode = create<AppModeStore>()((set) => ({ export const useAppMode = create<AppModeStore>()((set) => ({
mode: AppMode.Edit, mode: getModeFromSession(),
cursor: null, cursor: null,
editId: null, editId: null,
setMode: (mode: AppMode) => setMode: (mode: AppMode) =>
set((state) => { set((state) => {
persistModeToSession(mode);
return mode === AppMode.Edit return mode === AppMode.Edit
? { ? {
editId: state.cursor, editId: state.cursor,
+1 -1
View File
@@ -1,4 +1,4 @@
export const githubUrl = 'https://www.github.com/cpvalente/ontime'; export const githubUrl = 'https://www.github.com/cpvalente/ontime';
export const apiRepoLatest = 'https://api.github.com/repos/cpvalente/ontime/releases/latest'; export const apiRepoLatest = 'https://api.github.com/repos/cpvalente/ontime/releases/latest';
export const gitbookUrl = 'https://cpvalente.gitbook.io'; export const gitbookUrl = 'https://ontime.gitbook.io';
@@ -34,17 +34,26 @@ export default function PlaybackButtons(props: PlaybackButtonsProps) {
const isLast = selectedEventIndex === numEvents - 1; const isLast = selectedEventIndex === numEvents - 1;
const noEvents = numEvents === 0; const noEvents = numEvents === 0;
const disableGo = isRolling || noEvents || isLast; const disableGo = isRolling || noEvents || (isLast && !isArmed);
const disablePrev = isRolling || noEvents || isFirst; const disablePrev = isRolling || noEvents || isFirst;
const goModeText = selectedEventIndex === null || isArmed ? 'Start' : 'Next';
const goModeAction = () => {
if (isArmed) {
setPlayback.start();
} else {
setPlayback.startNext();
}
};
return ( return (
<div className={styles.buttonContainer}> <div className={styles.buttonContainer}>
<TapButton disabled={disableGo} onClick={() => setPlayback.startNext()} aspect='fill' className={styles.go}> <TapButton disabled={disableGo} onClick={goModeAction} aspect='fill' className={styles.go}>
GO {goModeText}
</TapButton> </TapButton>
<div className={style.playbackContainer}> <div className={style.playbackContainer}>
<TapButton <TapButton
onClick={() => setPlayback.start()} onClick={setPlayback.start}
disabled={isStopped || isRolling} disabled={isStopped || isRolling}
theme={Playback.Play} theme={Playback.Play}
active={isPlaying} active={isPlaying}
@@ -53,7 +62,7 @@ export default function PlaybackButtons(props: PlaybackButtonsProps) {
</TapButton> </TapButton>
<TapButton <TapButton
onClick={() => setPlayback.pause()} onClick={setPlayback.pause}
disabled={isStopped || isRolling || isArmed} disabled={isStopped || isRolling || isArmed}
theme={Playback.Pause} theme={Playback.Pause}
active={isPaused} active={isPaused}
@@ -63,19 +72,19 @@ export default function PlaybackButtons(props: PlaybackButtonsProps) {
</div> </div>
<div className={style.transportContainer}> <div className={style.transportContainer}>
<Tooltip label='Previous event' openDelay={tooltipDelayMid}> <Tooltip label='Previous event' openDelay={tooltipDelayMid}>
<TapButton onClick={() => setPlayback.previous()} disabled={disablePrev}> <TapButton onClick={setPlayback.previous} disabled={disablePrev}>
<IoPlaySkipBack /> <IoPlaySkipBack />
</TapButton> </TapButton>
</Tooltip> </Tooltip>
<Tooltip label='Next event' openDelay={tooltipDelayMid}> <Tooltip label='Next event' openDelay={tooltipDelayMid}>
<TapButton onClick={() => setPlayback.next()} disabled={disableGo}> <TapButton onClick={setPlayback.next} disabled={disableGo}>
<IoPlaySkipForward /> <IoPlaySkipForward />
</TapButton> </TapButton>
</Tooltip> </Tooltip>
</div> </div>
<div className={styles.extra}> <div className={styles.extra}>
<TapButton <TapButton
onClick={() => setPlayback.roll()} onClick={setPlayback.roll}
disabled={!isStopped || noEvents} disabled={!isStopped || noEvents}
theme={Playback.Roll} theme={Playback.Roll}
active={isRolling} active={isRolling}
@@ -83,12 +92,12 @@ export default function PlaybackButtons(props: PlaybackButtonsProps) {
<IoTimeOutline /> <IoTimeOutline />
</TapButton> </TapButton>
<Tooltip label='Reload event' openDelay={tooltipDelayMid}> <Tooltip label='Reload event' openDelay={tooltipDelayMid}>
<TapButton onClick={() => setPlayback.reload()} disabled={isStopped || isRolling}> <TapButton onClick={setPlayback.reload} disabled={isStopped || isRolling}>
<IoReload className={style.invertX} /> <IoReload className={style.invertX} />
</TapButton> </TapButton>
</Tooltip> </Tooltip>
<Tooltip label='Unload Event' openDelay={tooltipDelayMid}> <Tooltip label='Unload Event' openDelay={tooltipDelayMid}>
<TapButton onClick={() => setPlayback.stop()} disabled={isStopped && !isRolling} theme={Playback.Stop}> <TapButton onClick={setPlayback.stop} disabled={isStopped && !isRolling} theme={Playback.Stop}>
<IoStop /> <IoStop />
</TapButton> </TapButton>
</Tooltip> </Tooltip>
@@ -1,9 +1,9 @@
import { memo } from 'react'; import { memo } from 'react';
import { Box, IconButton } from '@chakra-ui/react'; import { Box, IconButton } from '@chakra-ui/react';
import { FiX } from '@react-icons/all-files/fi/FiX'; import { IoClose } from '@react-icons/all-files/io5/IoClose';
import ErrorBoundary from '../../common/components/error-boundary/ErrorBoundary'; import ErrorBoundary from '../../common/components/error-boundary/ErrorBoundary';
import { AppMode, useAppMode } from '../../common/stores/appModeStore'; import { useAppMode } from '../../common/stores/appModeStore';
import { cx } from '../../common/utils/styleUtils'; import { cx } from '../../common/utils/styleUtils';
import EventEditor from './EventEditor'; import EventEditor from './EventEditor';
@@ -19,13 +19,11 @@ const closeBtnStyle = {
}; };
const EventEditorExport = () => { const EventEditorExport = () => {
const appMode = useAppMode((state) => state.mode);
const editId = useAppMode((state) => state.editId); const editId = useAppMode((state) => state.editId);
const setEditId = useAppMode((state) => state.setEditId); const setEditId = useAppMode((state) => state.setEditId);
const editorStyle = cx([style.eventEditor, !editId ? style.noEvent : null]); const editorStyle = cx([style.eventEditor, !editId ? style.noEvent : null]);
const removeOpenEvent = () => setEditId(null); const removeOpenEvent = () => setEditId(null);
const canRemoveOpenId = appMode === AppMode.Run;
return ( return (
<Box className={editorStyle}> <Box className={editorStyle}>
@@ -33,13 +31,7 @@ const EventEditorExport = () => {
<div className={style.eventEditorLayout}> <div className={style.eventEditorLayout}>
<EventEditor /> <EventEditor />
<div className={style.header}> <div className={style.header}>
<IconButton <IconButton aria-label='Close Menu' icon={<IoClose />} onClick={removeOpenEvent} {...closeBtnStyle} />
aria-label='Close Menu'
icon={<FiX />}
onClick={removeOpenEvent}
isDisabled={!canRemoveOpenId}
{...closeBtnStyle}
/>
</div> </div>
</div> </div>
</ErrorBoundary> </ErrorBoundary>
@@ -1,6 +1,7 @@
import { ReactNode } from 'react'; import { MouseEvent, ReactNode } from 'react';
import { IoOpenOutline } from '@react-icons/all-files/io5/IoOpenOutline'; import { IoOpenOutline } from '@react-icons/all-files/io5/IoOpenOutline';
import { openLink } from '../../common/utils/linkUtils';
import { cx } from '../../common/utils/styleUtils'; import { cx } from '../../common/utils/styleUtils';
import style from './ModalLink.module.scss'; import style from './ModalLink.module.scss';
@@ -14,8 +15,14 @@ interface ModalLinkProps {
export default function ModalLink(props: ModalLinkProps) { export default function ModalLink(props: ModalLinkProps) {
const { href, inline, children } = props; const { href, inline, children } = props;
const classes = cx([style.link, inline ? style.inline : null]); const classes = cx([style.link, inline ? style.inline : null]);
const handleClick = (event: MouseEvent) => {
event.preventDefault();
openLink(href);
};
return ( return (
<a href={href} target='_blank' rel='noreferrer' className={classes}> <a href='#!' target='_blank' rel='noreferrer' className={classes} onClick={handleClick}>
{children} <IoOpenOutline /> {children} <IoOpenOutline />
</a> </a>
); );
@@ -43,7 +43,7 @@ export default function AboutModal(props: AboutModalProps) {
<div className={styles.padBottom}> <div className={styles.padBottom}>
<span className={styles.sectionTitle}>Ontime</span> <span className={styles.sectionTitle}>Ontime</span>
Free Open Source Software for managing rundowns and event timers Free Open Source Software for managing rundowns and event timers
<ModalLink href='www.getontime.no'>www.getontime.no</ModalLink> <ModalLink href='https://www.getontime.no'>www.getontime.no</ModalLink>
</div> </div>
<div className={styles.padBottom}> <div className={styles.padBottom}>
<span className={styles.sectionTitle}>Current version</span> <span className={styles.sectionTitle}>Current version</span>
@@ -12,7 +12,7 @@ interface IntegrationModalProps {
onClose: () => void; onClose: () => void;
} }
const oscDocsUrl = 'https://cpvalente.gitbook.io/ontime/control-and-feedback/osc'; const oscDocsUrl = 'https://ontime.gitbook.io/v2/control-and-feedback/osc';
export default function IntegrationModal(props: IntegrationModalProps) { export default function IntegrationModal(props: IntegrationModalProps) {
const { isOpen, onClose } = props; const { isOpen, onClose } = props;
+27 -15
View File
@@ -1,4 +1,4 @@
import { useCallback, useEffect, useRef, useState } from 'react'; import { MutableRefObject, useCallback, useEffect, useRef, useState } from 'react';
import { closestCenter, DndContext, DragEndEvent, PointerSensor, useSensor, useSensors } from '@dnd-kit/core'; import { closestCenter, DndContext, DragEndEvent, PointerSensor, useSensor, useSensors } from '@dnd-kit/core';
import { arrayMove, SortableContext, verticalListSortingStrategy } from '@dnd-kit/sortable'; import { arrayMove, SortableContext, verticalListSortingStrategy } from '@dnd-kit/sortable';
import { OntimeRundown, Playback, SupportedEvent } from 'ontime-types'; import { OntimeRundown, Playback, SupportedEvent } from 'ontime-types';
@@ -37,7 +37,8 @@ export default function Rundown(props: RundownProps) {
const appMode = useAppMode((state) => state.mode); const appMode = useAppMode((state) => state.mode);
const viewFollowsCursor = appMode === AppMode.Run; const viewFollowsCursor = appMode === AppMode.Run;
const moveCursorTo = useAppMode((state) => state.setCursor); const moveCursorTo = useAppMode((state) => state.setCursor);
const cursorRef = useRef<HTMLDivElement>(); const cursorRef = useRef<HTMLDivElement | null>(null);
const scrollRef = useRef<HTMLDivElement | null>(null);
// DND KIT // DND KIT
const sensors = useSensors(useSensor(PointerSensor)); const sensors = useSensors(useSensor(PointerSensor));
@@ -153,19 +154,25 @@ export default function Rundown(props: RundownProps) {
// when cursor moves, view should follow // when cursor moves, view should follow
useEffect(() => { useEffect(() => {
if (!cursorRef?.current) return; function scrollToComponent(
componentRef: MutableRefObject<HTMLDivElement>,
scrollRef: MutableRefObject<HTMLDivElement>,
) {
const componentRect = componentRef.current.getBoundingClientRect();
const scrollRect = scrollRef.current.getBoundingClientRect();
const top = componentRect.top - scrollRect.top + scrollRef.current.scrollTop - 100;
scrollRef.current.scrollTo({ top, behavior: 'smooth' });
}
// using start in block parameter causes jumpy behaviour if (cursorRef.current && scrollRef.current) {
// could alternatively scroll using scrollTo and // Use requestAnimationFrame to ensure the component is fully loaded
// calculate position within a range window.requestAnimationFrame(() => {
// if the item is near the top half, we are ok scrollToComponent(cursorRef as MutableRefObject<HTMLDivElement>, scrollRef as MutableRefObject<HTMLDivElement>);
// otherwise scroll difference });
cursorRef.current.scrollIntoView({ }
behavior: 'smooth',
block: 'nearest', // eslint-disable-next-line -- the prompt seems incorrect
inline: 'start', }, [cursorRef?.current, scrollRef]);
});
}, [cursorRef]);
useEffect(() => { useEffect(() => {
// in run mode, we follow selection // in run mode, we follow selection
@@ -200,9 +207,10 @@ export default function Rundown(props: RundownProps) {
let thisEnd = 0; let thisEnd = 0;
let previousEventId: string | undefined; let previousEventId: string | undefined;
let eventIndex = -1; let eventIndex = -1;
let isPast = Boolean(featureData?.selectedEventId);
return ( return (
<div className={style.eventContainer}> <div className={style.eventContainer} ref={scrollRef}>
<DndContext onDragEnd={handleOnDragEnd} sensors={sensors} collisionDetection={closestCenter}> <DndContext onDragEnd={handleOnDragEnd} sensors={sensors} collisionDetection={closestCenter}>
<SortableContext items={statefulEntries} strategy={verticalListSortingStrategy}> <SortableContext items={statefulEntries} strategy={verticalListSortingStrategy}>
<div className={style.list}> <div className={style.list}>
@@ -225,12 +233,16 @@ export default function Rundown(props: RundownProps) {
const isSelected = featureData?.selectedEventId === entry.id; const isSelected = featureData?.selectedEventId === entry.id;
const isNext = featureData?.nextEventId === entry.id; const isNext = featureData?.nextEventId === entry.id;
const hasCursor = entry.id === cursor; const hasCursor = entry.id === cursor;
if (isSelected) {
isPast = false;
}
return ( return (
<div key={entry.id} ref={hasCursor ? cursorRef : undefined}> <div key={entry.id} ref={hasCursor ? cursorRef : undefined}>
<RundownEntry <RundownEntry
type={entry.type} type={entry.type}
eventIndex={eventIndex} eventIndex={eventIndex}
isPast={isPast}
data={entry} data={entry}
selected={isSelected} selected={isSelected}
hasCursor={hasCursor} hasCursor={hasCursor}
@@ -17,6 +17,7 @@ export type EventItemActions = 'set-cursor' | 'event' | 'delay' | 'block' | 'del
interface RundownEntryProps { interface RundownEntryProps {
type: SupportedEvent; type: SupportedEvent;
eventIndex: number; eventIndex: number;
isPast: boolean;
data: OntimeRundownEntry; data: OntimeRundownEntry;
selected: boolean; selected: boolean;
hasCursor: boolean; hasCursor: boolean;
@@ -32,6 +33,7 @@ interface RundownEntryProps {
export default function RundownEntry(props: RundownEntryProps) { export default function RundownEntry(props: RundownEntryProps) {
const { const {
eventIndex, eventIndex,
isPast,
data, data,
selected, selected,
hasCursor, hasCursor,
@@ -168,6 +170,7 @@ export default function RundownEntry(props: RundownEntryProps) {
delay={delay} delay={delay}
previousEnd={previousEnd} previousEnd={previousEnd}
colour={data.colour} colour={data.colour}
isPast={isPast}
next={next} next={next}
skip={data.skip} skip={data.skip}
selected={selected} selected={selected}
@@ -10,9 +10,9 @@ $skip-opacity: 0.1;
display: grid; display: grid;
grid-template-areas: grid-template-areas:
"binder ... ... ..." "binder ... ... ..."
"binder pb-actions times actions" "binder pb-actions times actions"
"binder pb-actions title title" "binder pb-actions title next"
"binder pb-actions estatus estatus" "binder pb-actions estatus estatus"
"binder ... ... ..."; "binder ... ... ...";
@@ -25,7 +25,7 @@ $skip-opacity: 0.1;
transition-property: background-color; transition-property: background-color;
transition-duration: $transition-time-feedback; transition-duration: $transition-time-feedback;
@mixin declare-overrides(){ @mixin declare-overrides() {
--status-color-override: #{$gray-200}; --status-color-override: #{$gray-200};
--status-color-active-override: #{$green-400}; --status-color-active-override: #{$green-400};
} }
@@ -45,7 +45,7 @@ $skip-opacity: 0.1;
} }
&.pause { &.pause {
background-color: $orange-700; background-color: rgba($ontime-paused, 0.6);
@include declare-overrides; @include declare-overrides;
} }
@@ -53,6 +53,10 @@ $skip-opacity: 0.1;
outline: 1px solid $block-cursor-color; outline: 1px solid $block-cursor-color;
} }
&.past:not(.skip) {
opacity: 0.6;
}
&.skip { &.skip {
border: 1px solid $white-3; border: 1px solid $white-3;
@@ -121,7 +125,6 @@ $skip-opacity: 0.1;
justify-content: flex-end; justify-content: flex-end;
} }
.eventOptions { .eventOptions {
margin: $element-spacing 16px $element-spacing 0; margin: $element-spacing 16px $element-spacing 0;
} }
@@ -163,6 +166,16 @@ $skip-opacity: 0.1;
line-height: 13px; line-height: 13px;
} }
.nextTag {
grid-area: next;
font-size: 1em;
color: $orange-500;
letter-spacing: 0.03px;
font-weight: 600;
text-align: right;
}
.eventStatus { .eventStatus {
grid-area: status; grid-area: status;
display: flex; display: flex;
@@ -171,11 +184,6 @@ $skip-opacity: 0.1;
gap: 8px; gap: 8px;
color: var(--status-color-override, $gray-500); color: var(--status-color-override, $gray-500);
.tag {
padding-top: 1px;
font-size: 0.55em;
color: $active-indicator;
}
.statusIcon { .statusIcon {
width: 16px; width: 16px;
@@ -26,6 +26,7 @@ interface EventBlockProps {
delay: number; delay: number;
previousEnd: number; previousEnd: number;
colour: string; colour: string;
isPast: boolean;
next: boolean; next: boolean;
skip: boolean; skip: boolean;
selected: boolean; selected: boolean;
@@ -59,6 +60,7 @@ export default function EventBlock(props: EventBlockProps) {
delay, delay,
previousEnd, previousEnd,
colour, colour,
isPast,
next, next,
skip = false, skip = false,
selected, selected,
@@ -128,6 +130,7 @@ export default function EventBlock(props: EventBlockProps) {
const blockClasses = cx([ const blockClasses = cx([
style.eventBlock, style.eventBlock,
skip ? style.skip : null, skip ? style.skip : null,
isPast ? style.past : null,
selected ? style.selected : null, selected ? style.selected : null,
playback ? style[playback] : null, playback ? style[playback] : null,
hasCursor ? style.hasCursor : null, hasCursor ? style.hasCursor : null,
@@ -113,6 +113,11 @@ const EventBlockInner = (props: EventBlockInnerProps) => {
previousEnd={previousEnd} previousEnd={previousEnd}
/> />
<EditableBlockTitle title={title} eventId={eventId} placeholder='Event title' className={style.eventTitle} /> <EditableBlockTitle title={title} eventId={eventId} placeholder='Event title' className={style.eventTitle} />
{next && (
<Tooltip label='Next event' {...tooltipProps}>
<span className={style.nextTag}>UP NEXT</span>
</Tooltip>
)}
<EventBlockPlayback <EventBlockPlayback
eventId={eventId} eventId={eventId}
skip={skip} skip={skip}
@@ -127,11 +132,6 @@ const EventBlockInner = (props: EventBlockInnerProps) => {
{selected && <EventBlockProgressBar playback={playback} />} {selected && <EventBlockProgressBar playback={playback} />}
</div> </div>
<div className={style.eventStatus} tabIndex={-1}> <div className={style.eventStatus} tabIndex={-1}>
{next && (
<Tooltip label='Next event' {...tooltipProps}>
<span className={style.tag}>NEXT</span>
</Tooltip>
)}
<Tooltip label={`Time type: ${timerType}`} {...tooltipProps}> <Tooltip label={`Time type: ${timerType}`} {...tooltipProps}>
<span> <span>
<TimerIcon type={timerType} className={style.statusIcon} /> <TimerIcon type={timerType} className={style.statusIcon} />
@@ -1,5 +1,5 @@
import { ComponentType, useMemo } from 'react'; import { ComponentType, useMemo } from 'react';
import { Playback, TitleBlock } from 'ontime-types'; import { TitleBlock } from 'ontime-types';
import { useStore } from 'zustand'; import { useStore } from 'zustand';
import useEventData from '../../common/hooks-query/useEventData'; import useEventData from '../../common/hooks-query/useEventData';
@@ -80,7 +80,6 @@ const withData = <P extends object>(Component: ComponentType<P>) => {
// get clock string // get clock string
const TimeManagerType = { const TimeManagerType = {
...timer, ...timer,
finished: playback === Playback.Play && (timer.current ?? 0) < 0 && timer.startedAt,
playback, playback,
}; };
@@ -91,7 +91,8 @@ export default function Countdown(props: CountdownProps) {
} }
const standby = time.playback !== Playback.Play && time.playback !== Playback.Roll && selectedId === follow?.id; const standby = time.playback !== Playback.Play && time.playback !== Playback.Roll && selectedId === follow?.id;
const isRunningFinished = time.finished && runningMessage === TimerMessage.running; const finished = time.playback === Playback.Play && (time.current ?? 0) < 0 && time.startedAt;
const isRunningFinished = finished && runningMessage === TimerMessage.running;
const isSelected = runningMessage === TimerMessage.running; const isSelected = runningMessage === TimerMessage.running;
const delayedTimerStyles = delay > 0 ? 'aux-timers__value--delayed' : ''; const delayedTimerStyles = delay > 0 ? 'aux-timers__value--delayed' : '';
@@ -130,8 +130,8 @@ export default function MinimalTimer(props: MinimalTimerProps) {
const isNegative = const isNegative =
(time.current ?? 0) < 0 && time.timerType !== TimerType.Clock && time.timerType !== TimerType.CountUp; (time.current ?? 0) < 0 && time.timerType !== TimerType.Clock && time.timerType !== TimerType.CountUp;
const showEndMessage = (time.current ?? 0) < 0 && viewSettings.endMessage && !hideEndMessage; const showEndMessage = (time.current ?? 0) < 0 && viewSettings.endMessage && !hideEndMessage;
const showFinished = const finished = time.playback === Playback.Play && (time.current ?? 0) < 0 && time.startedAt;
time.finished && !userOptions?.hideOvertime && (time.timerType !== TimerType.Clock || showEndMessage); const showFinished = finished && !userOptions?.hideOvertime && (time.timerType !== TimerType.Clock || showEndMessage);
const showProgress = time.playback !== Playback.Stop; const showProgress = time.playback !== Playback.Stop;
const showWarning = (time.current ?? 1) < viewSettings.warningThreshold; const showWarning = (time.current ?? 1) < viewSettings.warningThreshold;
@@ -65,9 +65,10 @@ export default function Timer(props: TimerProps) {
const isNegative = const isNegative =
(time.current ?? 0) < 0 && time.timerType !== TimerType.Clock && time.timerType !== TimerType.CountUp; (time.current ?? 0) < 0 && time.timerType !== TimerType.Clock && time.timerType !== TimerType.CountUp;
const finished = time.playback === Playback.Play && (time.current ?? 0) < 0 && time.startedAt;
const showEndMessage = (time.current ?? 1) < 0 && viewSettings.endMessage; const showEndMessage = (time.current ?? 1) < 0 && viewSettings.endMessage;
const showProgress = time.playback !== Playback.Stop; const showProgress = time.playback !== Playback.Stop;
const showFinished = time.finished && (time.timerType !== TimerType.Clock || showEndMessage); const showFinished = finished && (time.timerType !== TimerType.Clock || showEndMessage);
const showWarning = (time.current ?? 1) < viewSettings.warningThreshold; const showWarning = (time.current ?? 1) < viewSettings.warningThreshold;
const showDanger = (time.current ?? 1) < viewSettings.dangerThreshold; const showDanger = (time.current ?? 1) < viewSettings.dangerThreshold;
const timerColor = const timerColor =
@@ -130,7 +131,7 @@ export default function Timer(props: TimerProps) {
/> />
<AnimatePresence> <AnimatePresence>
{title.showNow && !time.finished && ( {title.showNow && !finished && (
<motion.div <motion.div
className='event now' className='event now'
key='now' key='now'
+1 -8
View File
@@ -96,13 +96,6 @@ function getApplicationMenu(isMac, askToQuit) {
await shell.openExternal('http://localhost:4001/lower'); await shell.openExternal('http://localhost:4001/lower');
}, },
}, },
{
label: 'PiP',
click: async () => {
await shell.openExternal('http://localhost:4001/pip');
},
},
{ {
label: 'Studio Clock', label: 'Studio Clock',
click: async () => { click: async () => {
@@ -160,7 +153,7 @@ function getApplicationMenu(isMac, askToQuit) {
{ {
label: 'Online documentation', label: 'Online documentation',
click: async () => { click: async () => {
await shell.openExternal('https://cpvalente.gitbook.io/ontime/'); await shell.openExternal('https://ontime.gitbook.io/');
}, },
}, },
], ],