mirror of
https://github.com/cpvalente/ontime.git
synced 2026-08-14 03:43:50 +00:00
Feat/mac (#109)
* chore: upgrade dependencies * feat/mac: draft pipeline * feat/mac: use public folder for transient files * feat/mac: set app fullscreen * feat/mac: cmd + , toggles menu * feat/mac: update readme * refact: easier login process * refact prevent style issues with safari * refact reduce css download * style: cleanup paginator design * style: studio clock is responsive * style: fix spacing style issues with safari
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import React, { memo, useContext } from 'react';
|
||||
import { Divider } from '@chakra-ui/react';
|
||||
import { ButtonGroup, Divider, HStack } from '@chakra-ui/react';
|
||||
import { CursorContext } from '../../app/context/CursorContext';
|
||||
import MenuActionButtons from './MenuActionButtons';
|
||||
import CollapseBtn from 'common/components/buttons/CollapseBtn';
|
||||
@@ -42,20 +42,25 @@ const EventListMenu = ({ eventsHandler }) => {
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={style.headerButtons}>
|
||||
<ExpandBtn size='sm' clickhandler={() => eventsHandler('expandall')} />
|
||||
<CollapseBtn size='sm' clickhandler={() => eventsHandler('collapseall')} />
|
||||
<HStack className={style.headerButtons}>
|
||||
<ButtonGroup isAttached>
|
||||
<ExpandBtn size='sm' clickhandler={() => eventsHandler('expandall')} />
|
||||
<CollapseBtn size='sm' clickhandler={() => eventsHandler('collapseall')} />
|
||||
</ButtonGroup>
|
||||
<Divider orientation='vertical' />
|
||||
<CursorUpBtn size='sm' clickhandler={() => actionHandler('cursorUp')} />
|
||||
<CursorDownBtn size='sm' clickhandler={() => actionHandler('cursorDown')} />
|
||||
<CursorLockedBtn
|
||||
size='sm'
|
||||
clickhandler={() => actionHandler('togglelock')}
|
||||
active={isCursorLocked}
|
||||
/>
|
||||
<ButtonGroup isAttached>
|
||||
<CursorUpBtn size='sm' clickhandler={() => actionHandler('cursorUp')} />
|
||||
<CursorDownBtn size='sm' clickhandler={() => actionHandler('cursorDown')} />
|
||||
<CursorLockedBtn
|
||||
size='sm'
|
||||
clickhandler={() => actionHandler('togglelock')}
|
||||
active={isCursorLocked}
|
||||
width='3em'
|
||||
/>
|
||||
</ButtonGroup>
|
||||
<Divider orientation='vertical' />
|
||||
<MenuActionButtons actionHandler={actionHandler} size='sm' />
|
||||
</div>
|
||||
</HStack>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,11 +1,4 @@
|
||||
.headerButtons {
|
||||
display: flex;
|
||||
gap: 0.5em;
|
||||
align-content: center;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.menu {
|
||||
color: #000;
|
||||
background-color: rgba(255, 255, 255, 0.67);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useContext, useRef } from 'react';
|
||||
import React, { useCallback, useContext, useEffect, useRef } from 'react';
|
||||
import { useMutation, useQueryClient } from 'react-query';
|
||||
import { downloadEvents, uploadEvents } from 'app/api/ontimeApi';
|
||||
import { EVENTS_TABLE } from 'app/api/apiConstants';
|
||||
@@ -12,9 +12,10 @@ import HelpIconBtn from './buttons/HelpIconBtn';
|
||||
import UploadIconBtn from './buttons/UploadIconBtn';
|
||||
import { LoggingContext } from '../../app/context/LoggingContext';
|
||||
import PropTypes from 'prop-types';
|
||||
import { VStack } from '@chakra-ui/react';
|
||||
|
||||
export default function MenuBar(props) {
|
||||
const { isOpen, onOpen } = props;
|
||||
const { isOpen, onOpen, onClose } = props;
|
||||
const { emitError } = useContext(LoggingContext);
|
||||
const hiddenFileInput = useRef(null);
|
||||
const queryClient = useQueryClient();
|
||||
@@ -35,7 +36,7 @@ export default function MenuBar(props) {
|
||||
};
|
||||
|
||||
const buttonStyle = {
|
||||
fontSize: '1.5em'
|
||||
fontSize: '1.5em',
|
||||
};
|
||||
|
||||
const handleUpload = (event) => {
|
||||
@@ -44,7 +45,7 @@ export default function MenuBar(props) {
|
||||
|
||||
// Limit file size to 1MB
|
||||
if (fileUploaded.size > 1000000) {
|
||||
emitError('Error: File size limit (1MB) exceeded')
|
||||
emitError('Error: File size limit (1MB) exceeded');
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -53,10 +54,10 @@ export default function MenuBar(props) {
|
||||
try {
|
||||
uploaddb.mutate(fileUploaded);
|
||||
} catch (error) {
|
||||
emitError(`Failed uploading file: ${error}`)
|
||||
emitError(`Failed uploading file: ${error}`);
|
||||
}
|
||||
} else {
|
||||
emitError('Error: File type unknown')
|
||||
emitError('Error: File type unknown');
|
||||
}
|
||||
|
||||
// reset input value
|
||||
@@ -65,7 +66,7 @@ export default function MenuBar(props) {
|
||||
|
||||
const handleIPC = (action) => {
|
||||
// Stop crashes when testing locally
|
||||
if (window.process?.type === undefined) {
|
||||
if (typeof window.process?.type === 'undefined') {
|
||||
if (action === 'help') {
|
||||
window.open('https://cpvalente.gitbook.io/ontime/');
|
||||
}
|
||||
@@ -92,27 +93,45 @@ export default function MenuBar(props) {
|
||||
}
|
||||
};
|
||||
|
||||
// Handle keyboard shortcuts
|
||||
const handleKeyPress = useCallback(
|
||||
(e) => {
|
||||
// handle held key
|
||||
if (e.repeat) return;
|
||||
// check if the alt key is pressed
|
||||
if (e.ctrlKey) {
|
||||
if (e.key === ',') {
|
||||
// if we are in electron
|
||||
if (window.process?.type === undefined) return;
|
||||
if (window.process.type === 'renderer') {
|
||||
// open if not open
|
||||
isOpen ? onClose() : onOpen();
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
[isOpen, onClose, onOpen]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
// attach the event listener
|
||||
document.addEventListener('keydown', handleKeyPress);
|
||||
|
||||
// remove the event listener
|
||||
return () => {
|
||||
document.removeEventListener('keydown', handleKeyPress);
|
||||
};
|
||||
}, [handleKeyPress]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<VStack>
|
||||
<QuitIconBtn size='lg' clickhandler={() => handleIPC('shutdown')} />
|
||||
<MaxIconBtn
|
||||
style={{ ...buttonStyle }}
|
||||
size='lg'
|
||||
clickhandler={() => handleIPC('max')}
|
||||
/>
|
||||
<MinIconBtn
|
||||
style={{ ...buttonStyle }}
|
||||
size='lg'
|
||||
clickhandler={() => handleIPC('min')}
|
||||
/>
|
||||
<MaxIconBtn style={{ ...buttonStyle }} size='lg' clickhandler={() => handleIPC('max')} />
|
||||
<MinIconBtn style={{ ...buttonStyle }} size='lg' clickhandler={() => handleIPC('min')} />
|
||||
<div className={style.gap} />
|
||||
<HelpIconBtn
|
||||
style={{ ...buttonStyle }}
|
||||
size='lg'
|
||||
clickhandler={() => handleIPC('help')}
|
||||
/>
|
||||
<HelpIconBtn style={{ ...buttonStyle }} size='lg' clickhandler={() => handleIPC('help')} />
|
||||
<SettingsIconBtn
|
||||
style={{...buttonStyle}}
|
||||
style={{ ...buttonStyle }}
|
||||
size='lg'
|
||||
className={isOpen ? style.open : ''}
|
||||
clickhandler={onOpen}
|
||||
@@ -126,22 +145,14 @@ export default function MenuBar(props) {
|
||||
onChange={handleUpload}
|
||||
accept='.json, .xlsx'
|
||||
/>
|
||||
<UploadIconBtn
|
||||
style={{ ...buttonStyle }}
|
||||
size='lg'
|
||||
clickhandler={handleClick}
|
||||
/>
|
||||
<DownloadIconBtn
|
||||
style={{ ...buttonStyle }}
|
||||
size='lg'
|
||||
clickhandler={handleDownload}
|
||||
/>
|
||||
</>
|
||||
<UploadIconBtn style={{ ...buttonStyle }} size='lg' clickhandler={handleClick} />
|
||||
<DownloadIconBtn style={{ ...buttonStyle }} size='lg' clickhandler={handleDownload} />
|
||||
</VStack>
|
||||
);
|
||||
}
|
||||
|
||||
MenuBar.propTypes = {
|
||||
isOpen: PropTypes.bool,
|
||||
onOpen: PropTypes.func.isRequired,
|
||||
onClose: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
|
||||
@@ -1,21 +1,23 @@
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import MenuBar from "../MenuBar";
|
||||
import {queryClientMock} from "../../../__mocks__/QueryClient.mock";
|
||||
import {QueryClientProvider} from "react-query";
|
||||
import MenuBar from '../MenuBar';
|
||||
import { queryClientMock } from '../../../__mocks__/QueryClient.mock';
|
||||
import { QueryClientProvider } from 'react-query';
|
||||
|
||||
const onOpenHandler = jest.fn();
|
||||
const onCloseHandler = jest.fn();
|
||||
|
||||
const renderInMock = () => {
|
||||
render(
|
||||
<QueryClientProvider client={queryClientMock}>
|
||||
<MenuBar onOpen={onOpenHandler} />
|
||||
<MenuBar onOpen={onOpenHandler} onClose={onCloseHandler} />
|
||||
</QueryClientProvider>
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
test('check that menu bar renders correctly', () => {
|
||||
// need to inject the react query provider
|
||||
renderInMock();
|
||||
|
||||
const nButtons = screen.getAllByRole("button").length;
|
||||
const nButtons = screen.getAllByRole('button').length;
|
||||
expect(nButtons).toBe(7);
|
||||
});
|
||||
|
||||
@@ -1,21 +1,23 @@
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import MenuBar from "../MenuBar";
|
||||
import {queryClientMock} from "../../../__mocks__/QueryClient.mock";
|
||||
import {QueryClientProvider} from "react-query";
|
||||
import MenuBar from '../MenuBar';
|
||||
import { queryClientMock } from '../../../__mocks__/QueryClient.mock';
|
||||
import { QueryClientProvider } from 'react-query';
|
||||
|
||||
const onOpenHandler = jest.fn();
|
||||
const onCloseHandler = jest.fn();
|
||||
|
||||
const renderInMock = () => {
|
||||
render(
|
||||
<QueryClientProvider client={queryClientMock}>
|
||||
<MenuBar onOpen={onOpenHandler} />
|
||||
<MenuBar onOpen={onOpenHandler} onClose={onCloseHandler} />
|
||||
</QueryClientProvider>
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
test('check that menu bar renders correctly', () => {
|
||||
// need to inject the react query provider
|
||||
renderInMock();
|
||||
|
||||
const nButtons = screen.getAllByRole("button").length;
|
||||
const nButtons = screen.getAllByRole('button').length;
|
||||
expect(nButtons).toBe(7);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user