V2 monorepo (#285)

* refactor(project structure): UI

* refactor(project structure): extract utilities

* refactor(project structure): remove unused

* refactor(project structure): electron

* refactor(project structure): server

refactor: migrate to vitest

refactor: monorepo config

* refactor: extract application menu

* refactor: exit process

* refactor: extract tray menu

* chore: electron build

* Added Seconds in studio clock #282
---------

Co-authored-by: Fabian Posenau <fabian@fphome.de>

---------

Co-authored-by: Fabian Posenau <fabian.p99@gmx.de>
Co-authored-by: Fabian Posenau <fabian@fphome.de>
This commit is contained in:
Carlos Valente
2023-02-14 22:02:15 +01:00
committed by GitHub
parent 3918758d32
commit de9a7a87fd
439 changed files with 11381 additions and 14294 deletions
@@ -0,0 +1,54 @@
import { createContext, useCallback, useEffect, useState } from 'react';
import useSettings from '../hooks-query/useSettings';
export const AppContext = createContext({
auth: false,
data: {
pinCode: null,
},
});
export const AppContextProvider = ({ children }) => {
const [auth, setAuth] = useState(true);
const { data } = useSettings();
useEffect(() => {
if (data == null) return;
const previousEntry = sessionStorage.getItem('ontime-entry');
if (previousEntry) {
if (previousEntry === data?.pinCode) {
setAuth(true);
} else {
sessionStorage.removeItem('ontime-entry');
}
} else if (data?.pinCode == null || data?.pinCode === '') {
setAuth(true);
} else {
setAuth(false);
}
}, [data]);
/**
* Validates a pincode
* @return boolean - whether the pin is valid
*/
const validate = useCallback(
(pin) => {
let correct;
if (data?.pinCode == null || data?.pinCode === '') {
correct = true;
} else {
correct = pin === data?.pinCode;
}
if (correct) {
sessionStorage.setItem('ontime-entry', pin);
}
setAuth(correct);
return correct;
},
[data],
);
return <AppContext.Provider value={{ auth, validate }}>{children}</AppContext.Provider>;
};
@@ -0,0 +1,85 @@
import { createContext, ReactNode, useCallback, useMemo, useState } from 'react';
import { useLocalStorage } from '../hooks/useLocalStorage';
interface CursorContextState {
cursor: number;
isCursorLocked: boolean;
toggleCursorLocked: (newValue?: boolean) => void;
setCursor: (index: number) => void;
moveCursorUp: () => void;
moveCursorDown: () => void;
moveCursorTo: (index: number) => void;
}
export const CursorContext = createContext<CursorContextState>({
cursor: 0,
isCursorLocked: false,
toggleCursorLocked: () => undefined,
setCursor: () => undefined,
moveCursorUp: () => undefined,
moveCursorDown: () => undefined,
moveCursorTo: () => undefined,
});
interface CursorProviderProps {
children: ReactNode
}
export const CursorProvider = ({ children }: CursorProviderProps) => {
const [cursor, setCursor] = useState(0);
const [_cursorLocked, _setCursorLocked] = useLocalStorage('isCursorLocked', 'locked');
const isCursorLocked = useMemo(() => _cursorLocked === 'locked', [_cursorLocked]);
const cursorLockedOff = useCallback(() => _setCursorLocked('unlocked'), [_setCursorLocked]);
const cursorLockedOn = useCallback(() => _setCursorLocked('locked'), [_setCursorLocked]);
const moveCursorUp = useCallback(() => {
setCursor((prev) => Math.max(prev - 1, 0));
}, []);
const moveCursorDown = useCallback(() => {
setCursor((prev) => prev + 1);
}, []);
/**
* @param {boolean | undefined} newValue
*/
const toggleCursorLocked = useCallback(
(newValue?: boolean) => {
if (typeof newValue === 'undefined') {
if (isCursorLocked) {
cursorLockedOff();
} else {
cursorLockedOn();
}
} else if (!newValue) {
cursorLockedOff();
} else if (newValue) {
cursorLockedOn();
}
},
[cursorLockedOff, cursorLockedOn, isCursorLocked]
);
// moves cursor to given index
const moveCursorTo = useCallback((index: number) => {
setCursor(index);
}, []);
return (
<CursorContext.Provider
value={{
cursor,
isCursorLocked,
toggleCursorLocked,
setCursor,
moveCursorUp,
moveCursorDown,
moveCursorTo,
}}
>
{children}
</CursorContext.Provider>
);
};
@@ -0,0 +1,137 @@
import { createContext, ReactNode, useCallback, useEffect, useState } from 'react';
import { generateId } from 'ontime-utils';
import socket from '../utils/socket';
import { nowInMillis, stringFromMillis } from '../utils/time';
export enum LOG_LEVEL {
INFO = 'INFO',
WARN = 'WARN',
ERROR = 'ERROR',
}
export type Log = {
id: string;
origin: string;
time: string;
level: LOG_LEVEL;
text: string;
};
interface LoggingProviderState {
logData: Log[];
emitInfo: (text: string) => void;
emitWarning: (text: string) => void;
emitError: (text: string) => void;
clearLog: () => void;
}
type LoggingProviderProps = {
children: ReactNode;
};
const notInitialised = () => {
throw new Error('Not initialised');
};
export const LoggingContext = createContext<LoggingProviderState>({
logData: [],
emitInfo: notInitialised,
emitWarning: notInitialised,
emitError: notInitialised,
clearLog: notInitialised,
});
export const LoggingProvider = ({ children }: LoggingProviderProps) => {
const MAX_MESSAGES = 100;
const [logData, setLogData] = useState<Log[]>([]);
const origin = 'USER';
// todo: use react-query store
// todo: useSubscription or feature
// handle incoming messages
useEffect(() => {
socket.emit('get-logger');
socket.on('logger', (data: Log) => {
setLogData((currentLog) => [data, ...currentLog]);
});
// Clear listener
return () => {
socket.off('logger');
};
}, []);
/**
* Utility function sends message over socket
* @param text
* @param level
* @private
*/
const _send = useCallback(
(text: string, level: LOG_LEVEL) => {
if (socket != null) {
const newLogMessage: Log = {
id: generateId(),
origin,
time: stringFromMillis(nowInMillis()),
level,
text,
};
setLogData((currentLog) => [newLogMessage, ...currentLog]);
socket.emit('logger', newLogMessage);
}
if (logData.length > MAX_MESSAGES) {
setLogData((currentLog) => currentLog.slice(1));
}
},
[logData.length, setLogData],
);
/**
* Sends a message with level INFO
* @param text
*/
const emitInfo = useCallback(
(text: string) => {
_send(text, LOG_LEVEL.INFO);
},
[_send],
);
/**
* Sends a message with level WARN
* @param text
*/
const emitWarning = useCallback(
(text: string) => {
_send(text, LOG_LEVEL.WARN);
},
[_send],
);
/**
* Sends a message with level ERROR
* @param text
*/
const emitError = useCallback(
(text: string) => {
_send(text, LOG_LEVEL.ERROR);
},
[_send],
);
/**
* Clears running log
*/
const clearLog = useCallback(() => {
setLogData([]);
}, [setLogData]);
return (
<LoggingContext.Provider value={{ emitInfo, logData, emitWarning, emitError, clearLog }}>
{children}
</LoggingContext.Provider>
);
};
@@ -0,0 +1,79 @@
import { createContext, useCallback, useState } from 'react';
import { useLocalStorage } from '../hooks/useLocalStorage';
export const TableSettingsContext = createContext({
theme: '',
showSettings: false,
followSelected: false,
toggleSettings: () => undefined,
toggleTheme: () => undefined,
toggleFollow: () => undefined,
});
export const TableSettingsProvider = ({ children }) => {
const [theme, setTheme] = useLocalStorage('table-color-theme', 'dark');
const [followSelected, setFollowSelected] = useLocalStorage('table-follow-selected', false);
const [showSettings, setShowSettings] = useState(false);
/**
* @description Toggles the current value of dark mode
* @param {string} val - 'light' or 'dark'
*/
const toggleTheme = useCallback(
(val) => {
if (val === undefined) {
setTheme((prev) => (prev === 'light' ? 'dark' : 'light'));
} else {
setTheme(val);
}
},
[setTheme]
);
/**
* @description Toggles visibility state for settings
* @param {boolean} val - whether the settings window is visible
*/
const toggleSettings = useCallback(
(val) => {
if (val === undefined) {
setShowSettings((prev) => !prev);
} else {
setShowSettings(val);
}
},
[setShowSettings]
);
/**
* @description Toggles follow option
* @param {boolean} val - whether the window follows selected event
*/
const toggleFollow = useCallback(
(val) => {
if (val === undefined) {
setFollowSelected((prev) => !prev);
} else {
setFollowSelected(val);
}
},
[setFollowSelected]
);
return (
<TableSettingsContext.Provider
value={{
theme,
showSettings,
followSelected,
toggleSettings,
toggleTheme,
toggleFollow,
}}
>
{children}
</TableSettingsContext.Provider>
);
};