Feat/block improvements (#95)

* feat/89-block-improvements style: replace reload icon
* feat/block-feat/block-improvements add cursor buttons
* feat/block-improvements hover to create
* feat/block-improvements change settings in modal
* feat/block-improvements version bump
This commit is contained in:
Carlos Valente
2022-01-22 23:23:31 +01:00
committed by GitHub
parent 906a6631a7
commit 6a6f68b2f9
34 changed files with 652 additions and 255 deletions
@@ -2,7 +2,7 @@ import { Draggable } from 'react-beautiful-dnd';
import { FiMoreVertical } from 'react-icons/fi';
import DeleteIconBtn from 'common/components/buttons/DeleteIconBtn';
import ActionButtons from '../list/ActionButtons';
import style from './BlockBlock.module.css';
import style from './BlockBlock.module.scss';
import PropTypes from 'prop-types';
export default function BlockBlock(props) {
@@ -1,3 +1,4 @@
@use '../../../styles/main' as *;
/* ============= COMMON ============= */
.block {
@@ -8,7 +9,7 @@
width: 100%;
border-radius: 8px;
font-size: 15px;
border: 1px solid rgba(255, 255, 255, 0.05);
border: $block-border;
display: grid;
grid-template-columns: 2em auto;
@@ -16,7 +17,7 @@
align-items: baseline;
gap: 0.5em;
background-color: #805ad5;
background-color: $block-block-color;
background: linear-gradient(
0deg,
rgba(107, 70, 193, 0.4) 0%,
@@ -34,7 +35,6 @@
/* ============== ACTION ================ */
.actionOverlay {
display: flex;
display: flex;
flex-direction: row;
flex-wrap: wrap;
@@ -5,7 +5,7 @@ import ActionButtons from '../list/ActionButtons';
import DeleteIconBtn from 'common/components/buttons/DeleteIconBtn';
import ApplyIconBtn from 'common/components/buttons/ApplyIconBtn';
import DelayInput from 'common/input/DelayInput';
import style from './DelayBlock.module.css';
import style from './DelayBlock.module.scss';
import PropTypes from 'prop-types';
export default function DelayBlock(props) {
@@ -1,3 +1,5 @@
@use '../../../styles/main' as *;
/* ============= COMMON ============= */
.delay {
@@ -7,7 +9,7 @@
width: 100%;
border-radius: 8px;
font-size: 15px;
border: 1px solid rgba(255, 255, 255, 0.05);
border: $block-border;
display: grid;
grid-template-columns: 2em 1fr auto;
@@ -17,7 +19,7 @@
align-items: baseline;
gap: 0.5em;
background-color: #ecc94b;
background-color: $block-delay-color;
background: linear-gradient(
180deg,
rgba(214, 158, 46, 0.4) 0%,
@@ -30,7 +32,7 @@
.drag {
grid-area: drag;
color: rgba(255, 255, 255, 0.67);
color: $block-icon-drag;
align-self: center;
}
+49 -45
View File
@@ -6,10 +6,10 @@ import MenuBar from 'features/menu/MenuBar';
import ModalManager from 'features/modals/ModalManager';
import ErrorBoundary from 'common/components/errorBoundary/ErrorBoundary';
import { LoggingProvider } from '../../app/context/LoggingContext';
import { LocalEventSettingsProvider } from '../../app/context/LocalEventSettingsContext';
import { CursorProvider } from '../../app/context/CursorContext';
const EventListWrapper = lazy(() =>
import('features/editors/list/EventListWrapper')
);
const EventListWrapper = lazy(() => import('features/editors/list/EventListWrapper'));
const PlaybackControl = lazy(() => import('features/control/PlaybackControl'));
const MessageControl = lazy(() => import('features/control/MessageControl'));
const Info = lazy(() => import('features/info/Info'));
@@ -24,53 +24,57 @@ export default function Editor() {
return (
<LoggingProvider>
<ErrorBoundary>
<ModalManager isOpen={isOpen} onClose={onClose} />
</ErrorBoundary>
<LocalEventSettingsProvider>
<ErrorBoundary>
<ModalManager isOpen={isOpen} onClose={onClose} />
</ErrorBoundary>
<div className={styles.mainContainer}>
<Box id='settings' className={styles.settings}>
<ErrorBoundary>
<MenuBar onOpen={onOpen} isOpen={isOpen} />
</ErrorBoundary>
</Box>
<div className={styles.mainContainer}>
<CursorProvider>
<Box id='settings' className={styles.settings}>
<ErrorBoundary>
<MenuBar onOpen={onOpen} isOpen={isOpen} />
</ErrorBoundary>
</Box>
<Box className={styles.editor}>
<h1>Event List</h1>
<div className={styles.content}>
<ErrorBoundary>
<EventListWrapper />
</ErrorBoundary>
</div>
</Box>
<Box className={styles.editor}>
<h1>Event List</h1>
<div className={styles.content}>
<ErrorBoundary>
<EventListWrapper />
</ErrorBoundary>
</div>
</Box>
</CursorProvider>
<Box className={styles.messages}>
<h1>Display Messages</h1>
<div className={styles.content}>
<ErrorBoundary>
<MessageControl />
</ErrorBoundary>
</div>
</Box>
<Box className={styles.messages}>
<h1>Display Messages</h1>
<div className={styles.content}>
<ErrorBoundary>
<MessageControl />
</ErrorBoundary>
</div>
</Box>
<Box className={styles.playback}>
<h1>Timer Control</h1>
<div className={styles.content}>
<ErrorBoundary>
<PlaybackControl />
</ErrorBoundary>
</div>
</Box>
<Box className={styles.playback}>
<h1>Timer Control</h1>
<div className={styles.content}>
<ErrorBoundary>
<PlaybackControl />
</ErrorBoundary>
</div>
</Box>
<Box className={styles.info}>
<h1>Info</h1>
<div className={styles.content}>
<ErrorBoundary>
<Info />
</ErrorBoundary>
</div>
</Box>
</div>
<Box className={styles.info}>
<h1>Info</h1>
<div className={styles.content}>
<ErrorBoundary>
<Info />
</ErrorBoundary>
</div>
</Box>
</div>
</LocalEventSettingsProvider>
</LoggingProvider>
);
}
@@ -0,0 +1,73 @@
import { Tooltip } from '@chakra-ui/tooltip';
import { Checkbox } from '@chakra-ui/react';
import style from './EntryBlock.module.scss';
import { useContext, useEffect, useState } from 'react';
import { LocalEventSettingsContext } from '../../../app/context/LocalEventSettingsContext';
export default function EntryBlock(props) {
const { showKbd, index, eventsHandler } = props;
const { starTimeIsLastEnd, defaultPublic } = useContext(LocalEventSettingsContext);
const [doStartTime, setStartTime] = useState(starTimeIsLastEnd);
const [doPublic, setPublic] = useState(defaultPublic);
useEffect(() => {
setStartTime(starTimeIsLastEnd);
}, [starTimeIsLastEnd]);
useEffect(() => {
setPublic(defaultPublic);
}, [defaultPublic]);
return (
<div className={style.create}>
<Tooltip label='Add Event' openDelay={300}>
<span
className={style.createEvent}
onClick={() =>
eventsHandler(
'add',
{ type: 'event', order: index + 1, isPublic: doPublic },
{ startIsLastEnd: doStartTime ? index : undefined }
)
}
>
E{showKbd && <span className={style.keyboard}>Alt + E</span>}
</span>
</Tooltip>
<Tooltip label='Add Delay' openDelay={300}>
<span
className={style.createDelay}
onClick={() => eventsHandler('add', { type: 'delay', order: index + 1 })}
>
D{showKbd && <span className={style.keyboard}>Alt + D</span>}
</span>
</Tooltip>
<Tooltip label='Add Block' openDelay={300}>
<span
className={style.createBlock}
onClick={() => eventsHandler('add', { type: 'block', order: index + 1 })}
>
B{showKbd && <span className={style.keyboard}>Alt + B</span>}
</span>
</Tooltip>
<div className={style.options}>
<Checkbox
size='sm'
colorScheme='blue'
isChecked={doStartTime}
onChange={(e) => setStartTime(e.target.checked)}
>
Start time is last end
</Checkbox>
<Checkbox
size='sm'
colorScheme='blue'
isChecked={doPublic}
onChange={(e) => setPublic(e.target.checked)}
>
Default public
</Checkbox>
</div>
</div>
);
}
@@ -0,0 +1,92 @@
@use '../../../styles/main' as *;
.create {
padding: 0 0.5em;
box-sizing: border-box;
width: 100%;
border-radius: 8px;
font-size: 12px;
position: relative;
height: 20px;
margin: -8px 0;
transition: height 0.1s ease;
* {
display: none;
}
&:hover {
opacity: 1;
height: calc(2.5em + 20px);
display: flex;
align-items: center;
justify-content: center;
gap: 5%;
transition: height 0.15s ease;
* {
display: flex;
}
}
.createEvent,
.createDelay,
.createBlock {
width: auto;
padding: 0 16px;
height: 24px;
text-align: center;
vertical-align: center;
font-weight: 600;
line-height: 21px;
border-radius: 4px;
opacity: 0.6;
.keyboard {
margin-left: 4px;
padding: 0 4px;
color: #ccc;
border-radius: 2px;
font-family: Monospaced, sans-serif;
background-color: rgba(0, 0, 0, 0.24);
}
}
.createEvent {
border: 1px solid #2b6cb0;
color: lighten(#2b6cb0, 30%);
&:hover {
background-color: #2b6cb0;
opacity: 1;
}
}
.createDelay {
border: 1px solid #ecc94b;;
color: lighten(#ecc94b, 30%);
&:hover {
background-color: #ecc94b;
opacity: 1;
}
}
.createBlock {
border: 1px solid #805ad5;
color: lighten(#805ad5, 30%);
&:hover {
background-color: #805ad5;
opacity: 1;
}
}
.options {
display: flex;
flex-direction: column;
opacity: 0.65;
}
}
@@ -142,7 +142,7 @@
background-color: rgba(255, 255, 255, 0.03);
border: 1px solid rgba(255, 255, 255, 0.05);
border-radius: 4px;
border-radius: 8px;
width: 100%;
display: block;
@@ -150,11 +150,13 @@
overflow: hidden;
}
.collapsed .titleContasiner {
.collapsed .titleContainer {
height: fit-content;
}
.expanded .titleContainer {
height: 100%;
border-radius: 4px;
}
.oscLabel {
+43 -36
View File
@@ -1,21 +1,22 @@
import style from './List.module.scss';
import { createRef, useCallback, useEffect, useMemo, useState } from 'react';
import { createRef, useCallback, useContext, useEffect, useState } from 'react';
import { useSocket } from 'app/context/socketContext';
import Empty from 'common/state/Empty';
import EventListItem from './EventListItem';
import { DragDropContext, Droppable } from 'react-beautiful-dnd';
import { useAtom } from 'jotai';
import { SelectSetting } from 'app/context/settingsAtom';
import EntryBlock from '../EntryBlock/EntryBlock';
import { CursorContext } from '../../../app/context/CursorContext';
import { LocalEventSettingsContext } from '../../../app/context/LocalEventSettingsContext';
export default function EventList(props) {
const { events, eventsHandler } = props;
const { cursor, moveCursorUp, moveCursorDown, setCursor, isCursorLocked } =
useContext(CursorContext);
const socket = useSocket();
const [selectedId, setSelectedId] = useState(null);
const [nextId, setNextId] = useState(null);
const [cursor, setCursor] = useState(0);
const [cursorSettings] = useAtom(useMemo(() => SelectSetting('cursor'), []));
const cursorRef = createRef();
const { showQuickEntry } = useContext(LocalEventSettingsContext);
// Handle keyboard shortcuts
const handleKeyPress = useCallback(
@@ -26,13 +27,11 @@ export default function EventList(props) {
if (e.altKey && (!e.ctrlKey || !e.shiftKey)) {
// Arrow down
if (e.keyCode === 40) {
if (cursor == null) setCursor(0);
else if (cursor < events.length - 1) setCursor(cursor + 1);
if (cursor < events.length - 1) moveCursorDown();
}
// Arrow up
if (e.keyCode === 38) {
if (cursor == null) setCursor(0);
else if (cursor > 0) setCursor(cursor - 1);
if (cursor > 0) moveCursorUp();
}
// E
if (e.key === 'e' || e.key === 'E') {
@@ -54,7 +53,7 @@ export default function EventList(props) {
}
}
},
[cursor, events, eventsHandler]
[cursor, events.length, eventsHandler, moveCursorDown, moveCursorUp]
);
useEffect(() => {
@@ -67,7 +66,7 @@ export default function EventList(props) {
return () => {
document.removeEventListener('keydown', handleKeyPress);
};
}, [handleKeyPress, cursor, events]);
}, [handleKeyPress, cursor, events, setCursor]);
// handle incoming messages
useEffect(() => {
@@ -101,13 +100,14 @@ export default function EventList(props) {
block: 'nearest',
inline: 'start',
});
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [cursor]);
// if selected event
// or cursor settings changed
useEffect(() => {
// and if we are locked
if (cursorSettings !== 'locked' || selectedId == null) return;
if (!isCursorLocked || selectedId == null) return;
// move cursor
let gotoIndex = -1;
@@ -123,7 +123,8 @@ export default function EventList(props) {
// move cursor
setCursor(gotoIndex);
}
}, [selectedId, cursorSettings]);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [selectedId, isCursorLocked]);
if (events.length < 1) {
return <Empty text='No Events' />;
@@ -155,11 +156,7 @@ export default function EventList(props) {
<DragDropContext onDragEnd={handleOnDragEnd}>
<Droppable droppableId='eventlist'>
{(provided) => (
<div
className={style.list}
{...provided.droppableProps}
ref={provided.innerRef}
>
<div className={style.list} {...provided.droppableProps} ref={provided.innerRef}>
{events.map((e, index) => {
if (index === 0) {
cumulativeDelay = 0;
@@ -174,24 +171,34 @@ export default function EventList(props) {
previousEnd = thisEnd;
thisEnd = e.timeEnd;
}
return (
<div
ref={cursor === index ? cursorRef : undefined}
key={e.id}
className={cursor === index ? style.cursor : undefined}
>
<EventListItem
type={e.type}
index={index}
eventIndex={eventIndex}
data={e}
selected={selectedId === e.id}
next={nextId === e.id}
eventsHandler={eventsHandler}
delay={cumulativeDelay}
previousEnd={previousEnd}
/>
<div key={e.id}>
{index === 0 && showQuickEntry && (
<EntryBlock index={-1} eventsHandler={eventsHandler} />
)}
<div
ref={cursor === index ? cursorRef : undefined}
className={cursor === index ? style.cursor : undefined}
>
<EventListItem
type={e.type}
index={index}
eventIndex={eventIndex}
data={e}
selected={selectedId === e.id}
next={nextId === e.id}
eventsHandler={eventsHandler}
delay={cumulativeDelay}
previousEnd={previousEnd}
/>
</div>
{showQuickEntry && (
<EntryBlock
showKbd={index === cursor}
index={index}
eventsHandler={eventsHandler}
/>
)}
</div>
);
})}
@@ -3,6 +3,7 @@ import BlockBlock from '../BlockBlock/BlockBlock';
import EventBlock from '../EventBlock/EventBlock';
import { memo, useContext } from 'react';
import { LoggingContext } from '../../../app/context/LoggingContext';
import { LocalEventSettingsContext } from '../../../app/context/LocalEventSettingsContext';
const areEqual = (prevProps, nextProps) => {
return (
@@ -29,12 +30,21 @@ const EventListItem = (props) => {
...rest
} = props;
const { emitError } = useContext(LoggingContext);
const { starTimeIsLastEnd, defaultPublic } = useContext(LocalEventSettingsContext);
// Create / delete new events
const actionHandler = (action, payload) => {
switch (action) {
case 'event':
eventsHandler('add', { type: 'event', order: index + 1 });
eventsHandler(
'add',
{
type: 'event',
order: index + 1,
isPublic: defaultPublic,
},
{ startIsLastEnd: starTimeIsLastEnd ? index : undefined }
);
break;
case 'delay':
eventsHandler('add', { type: 'delay', order: index + 1 });
@@ -85,9 +95,7 @@ const EventListItem = (props) => {
/>
);
case 'block':
return (
<BlockBlock index={index} data={data} actionHandler={actionHandler} />
);
return <BlockBlock index={index} data={data} actionHandler={actionHandler} />;
case 'delay':
return (
<DelayBlock
@@ -1,32 +1,29 @@
import { useMutation, useQueryClient } from 'react-query';
import { useCallback, useContext, useEffect, useState } from 'react';
import {
fetchAllEvents,
requestPatch,
requestPost,
requestPut,
requestDelete,
requestDeleteAll,
requestReorder,
requestApplyDelay,
} from 'app/api/eventsApi.js';
import EventList from './EventList';
import EventListMenu from 'features/menu/EventListMenu.jsx';
import { useFetch } from 'app/hooks/useFetch.js';
import Empty from 'common/state/Empty';
import { useMutation, useQueryClient } from 'react-query';
import { EVENTS_TABLE } from 'app/api/apiConstants';
import { BatchOperation } from 'app/context/collapseAtom';
import { useAtom } from 'jotai';
import { LoggingContext } from '../../../app/context/LoggingContext';
import {
fetchAllEvents,
requestApplyDelay,
requestDelete,
requestDeleteAll,
requestPatch,
requestPost,
requestPut,
requestReorder,
} from 'app/api/eventsApi.js';
import { useFetch } from 'app/hooks/useFetch.js';
import EventList from './EventList';
import EventListMenu from 'features/menu/EventListMenu.jsx';
import Empty from 'common/state/Empty';
export default function EventListWrapper() {
const [, setCollapsed] = useAtom(BatchOperation);
const queryClient = useQueryClient();
const { emitError } = useContext(LoggingContext);
const { data, status, isError, refetch } = useFetch(
EVENTS_TABLE,
fetchAllEvents
);
const { data, status, isError, refetch } = useFetch(EVENTS_TABLE, fetchAllEvents);
const [events, setEvents] = useState(null);
const addEvent = useMutation(requestPost, {
@@ -72,10 +69,7 @@ export default function EventListWrapper() {
queryClient.cancelQueries([EVENTS_TABLE, newEvent.id]);
// Snapshot the previous value
const previousEvent = queryClient.getQueryData([
EVENTS_TABLE,
newEvent.id,
]);
const previousEvent = queryClient.getQueryData([EVENTS_TABLE, newEvent.id]);
// optimistically update object
queryClient.setQueryData([EVENTS_TABLE, newEvent.id], newEvent);
@@ -86,10 +80,7 @@ export default function EventListWrapper() {
// Mutation fails, rollback undos optimist update
onError: (error, newEvent, context) => {
queryClient.setQueryData(
[EVENTS_TABLE, context.newEvent.id],
context.previousEvent
);
queryClient.setQueryData([EVENTS_TABLE, context.newEvent.id], context.previousEvent);
},
// Mutation finished, failed or successful
// Fetch anyway, just to be sure
@@ -105,10 +96,7 @@ export default function EventListWrapper() {
queryClient.cancelQueries([EVENTS_TABLE, newEvent.id]);
// Snapshot the previous value
const previousEvent = queryClient.getQueryData([
EVENTS_TABLE,
newEvent.id,
]);
const previousEvent = queryClient.getQueryData([EVENTS_TABLE, newEvent.id]);
// optimistically update object
queryClient.setQueryData([EVENTS_TABLE, newEvent.id], newEvent);
@@ -119,10 +107,7 @@ export default function EventListWrapper() {
// Mutation fails, rollback undos optimist update
onError: (error, newEvent, context) => {
queryClient.setQueryData(
[EVENTS_TABLE, context.newEvent.id],
context.previousEvent
);
queryClient.setQueryData([EVENTS_TABLE, context.newEvent.id], context.previousEvent);
},
// Mutation finished, failed or successful
// Fetch anyway, just to be sure
@@ -237,11 +222,16 @@ export default function EventListWrapper() {
// Events API
const eventsHandler = useCallback(
async (action, payload) => {
async (action, payload, options = undefined) => {
switch (action) {
case 'add':
try {
await addEvent.mutateAsync(payload);
let newEvent = { ...payload };
// there is an option to pass an index of an array to use as start time
if (options?.startIsLastEnd !== undefined) {
newEvent.timeStart = data[options.startIsLastEnd].timeEnd || 0;
}
await addEvent.mutateAsync(newEvent);
} catch (error) {
emitError(`Error fetching data: ${error.message}`);
}
@@ -326,6 +316,7 @@ export default function EventListWrapper() {
break;
}
},
// eslint-disable-next-line react-hooks/exhaustive-deps
[data]
);
@@ -23,11 +23,11 @@
.cursor {
width: 100%;
background: linear-gradient(
180deg,
#ff7597 2%,
#0001 3%,
#0001 97%,
#ff7597 98%
180deg,
#ff7597 2%,
#0001 3%,
#0001 97%,
#ff7597 98%
);
border-radius: 14px;
+20 -20
View File
@@ -1,16 +1,16 @@
import { memo, useMemo } from 'react';
import { memo, useContext } from 'react';
import { Divider } from '@chakra-ui/react';
import style from './EventListMenu.module.css';
import { CursorContext } from '../../app/context/CursorContext';
import MenuActionButtons from './MenuActionButtons';
import CollapseBtn from 'common/components/buttons/CollapseBtn';
import ExpandBtn from 'common/components/buttons/ExpandBtn';
import { SelectSetting, HandleOptions } from 'app/context/settingsAtom';
import { useAtom } from 'jotai';
import LockIconBtn from 'common/components/buttons/LockIconBtn';
import CursorUpBtn from '../../common/components/buttons/CursorUpBtn';
import CursorDownBtn from '../../common/components/buttons/CursorDownBtn';
import CursorLockedBtn from 'common/components/buttons/CursorLockedBtn';
import style from './EventListMenu.module.css';
const EventListMenu = ({ eventsHandler }) => {
const [cursorSettings] = useAtom(useMemo(() => SelectSetting('cursor'), []));
const [, SetOption] = useAtom(HandleOptions);
const { isCursorLocked, toggleCursorLocked, moveCursorUp, moveCursorDown } =
useContext(CursorContext);
const actionHandler = (action) => {
switch (action) {
@@ -23,12 +23,14 @@ const EventListMenu = ({ eventsHandler }) => {
case 'block':
eventsHandler('add', { type: action, order: 0 });
break;
case 'cursorUp':
moveCursorUp();
break;
case 'cursorDown':
moveCursorDown();
break;
case 'togglelock':
let newSet = 'locked';
if (cursorSettings === 'locked') {
newSet = 'unlocked';
}
SetOption({ cursor: newSet });
toggleCursorLocked();
break;
case 'deleteall':
eventsHandler('deleteall');
@@ -40,16 +42,14 @@ const EventListMenu = ({ eventsHandler }) => {
return (
<div className={style.headerButtons}>
<CollapseBtn
size='sm'
clickhandler={() => eventsHandler('collapseall')}
/>
<ExpandBtn size='sm' clickhandler={() => eventsHandler('expandall')} />
<CollapseBtn size='sm' clickhandler={() => eventsHandler('collapseall')} />
<Divider orientation='vertical' />
<LockIconBtn
<CursorUpBtn size='sm' clickhandler={() => actionHandler('cursorUp')} />
<CursorDownBtn size='sm' clickhandler={() => actionHandler('cursorDown')} />
<CursorLockedBtn
size='sm'
clickhandler={() => actionHandler('togglelock')}
active={cursorSettings === 'locked'}
active={isCursorLocked}
/>
<Divider orientation='vertical' />
<MenuActionButtons actionHandler={actionHandler} size='sm' />
+104 -31
View File
@@ -1,16 +1,6 @@
import { ModalBody } from '@chakra-ui/modal';
import {
FormControl,
FormLabel,
Input,
PinInput,
PinInputField,
} from '@chakra-ui/react';
import {
getSettings,
ontimePlaceholderSettings,
postSettings,
} from 'app/api/ontimeApi';
import { Checkbox, FormControl, FormLabel, Input, PinInput, PinInputField } from '@chakra-ui/react';
import { getSettings, ontimePlaceholderSettings, postSettings } from 'app/api/ontimeApi';
import { useContext, useEffect, useState } from 'react';
import { useFetch } from 'app/hooks/useFetch';
import { APP_SETTINGS } from 'app/api/apiConstants';
@@ -20,6 +10,7 @@ import { IconButton } from '@chakra-ui/button';
import { FiEye } from 'react-icons/fi';
import SubmitContainer from './SubmitContainer';
import { inputProps } from './modalHelper';
import { LocalEventSettingsContext } from '../../app/context/LocalEventSettingsContext';
export default function AppSettingsModal() {
const { data, status, refetch } = useFetch(APP_SETTINGS, getSettings);
@@ -29,6 +20,19 @@ export default function AppSettingsModal() {
const [submitting, setSubmitting] = useState(false);
const [hidePin, setHidePin] = useState(true);
const {
showQuickEntry,
setShowQuickEntry,
starTimeIsLastEnd,
setStarTimeIsLastEnd,
defaultPublic,
setDefaultPublic,
} = useContext(LocalEventSettingsContext);
const [doShowQuickEntry, setDoShowQuickEntry] = useState(showQuickEntry);
const [doStarTimeIsLastEnd, setDoStarTimeIsLastEnd] = useState(starTimeIsLastEnd);
const [doDefaultPublic, setDoDefaultPublic] = useState(defaultPublic);
/**
* Set formdata from server state
*/
@@ -40,6 +44,24 @@ export default function AppSettingsModal() {
});
}, [changed, data]);
/**
* Set formdata from context
*/
useEffect(() => {
if (showQuickEntry == null) return;
setDoShowQuickEntry(showQuickEntry);
}, [showQuickEntry]);
useEffect(() => {
if (starTimeIsLastEnd == null) return;
setDoStarTimeIsLastEnd(starTimeIsLastEnd);
}, [starTimeIsLastEnd]);
useEffect(() => {
if (defaultPublic == null) return;
setDoDefaultPublic(defaultPublic);
}, [defaultPublic]);
/**
* Validate and submit data
*/
@@ -47,29 +69,38 @@ export default function AppSettingsModal() {
event.preventDefault();
setSubmitting(true);
// set context
setShowQuickEntry(doShowQuickEntry);
setStarTimeIsLastEnd(doStarTimeIsLastEnd);
setDefaultPublic(doDefaultPublic);
const f = formData;
let e = { status: false, message: '' };
// Validate fields
if (f.pinCode === '' || f.pinCode == null) {
e.status = true;
e.message += 'App pin code removed';
} else {
e.status = true;
e.message += 'App pin code added';
// we might not have changed this
if (f.pinCode !== data.pinCode) {
let e = { status: false, message: '' };
// Validate fields
if (f.pinCode === '' || f.pinCode == null) {
e.status = true;
e.message += 'App pin code removed';
} else {
e.status = true;
e.message += 'App pin code added';
}
// set fields with error
if (!e.status) {
emitError(`Invalid Input: ${e.message}`);
} else {
await postSettings(formData);
await refetch();
emitWarning(e.message);
setChanged(false);
}
}
// set fields with error
if (!e.status) {
emitError(`Invalid Input: ${e.message}`);
} else {
await postSettings(formData);
await refetch();
emitWarning(e.message);
setChanged(false);
}
setSubmitting(false);
setChanged(false);
};
/**
@@ -78,6 +109,11 @@ export default function AppSettingsModal() {
const revert = async () => {
setChanged(false);
await refetch();
// set from context
setDoShowQuickEntry(showQuickEntry);
setDoStarTimeIsLastEnd(starTimeIsLastEnd);
setDoDefaultPublic(defaultPublic);
};
/**
@@ -92,6 +128,13 @@ export default function AppSettingsModal() {
setChanged(true);
};
/**
* Sets changed flag to true
*/
const handleContextChange = () => {
setChanged(true);
};
const disableModal = status !== 'success';
return (
@@ -157,6 +200,36 @@ export default function AppSettingsModal() {
</div>
</FormControl>
</div>
<div className={style.hSeparator}>Create Event Default Settings</div>
<div className={style.modalColumn}>
<Checkbox
isChecked={doShowQuickEntry}
onChange={(e) => {
setDoShowQuickEntry(e.target.checked);
handleContextChange();
}}
>
Show quick entry on hover
</Checkbox>
<Checkbox
isChecked={doStarTimeIsLastEnd}
onChange={(e) => {
setDoStarTimeIsLastEnd(e.target.checked);
handleContextChange();
}}
>
Start time is last end
</Checkbox>
<Checkbox
isChecked={doDefaultPublic}
onChange={(e) => {
setDoDefaultPublic(e.target.checked);
handleContextChange();
}}
>
Event default public
</Checkbox>
</div>
</div>
<SubmitContainer
revert={revert}
@@ -32,6 +32,7 @@
gap: 8px;
align-items: center;
}
.error {
font-size: 0.8em;
color: $error-red;
@@ -72,6 +73,13 @@
padding: 0 0.5em 0.5em 0.5em;
}
.modalColumn {
display: flex;
flex-direction: column;
align-items: flex-start;
padding: 0 0.5em 0.5em 0.5em;
}
.spacedEntry {
padding: 0 0.5em 0.5em 0.5em;
}
@@ -8,7 +8,6 @@ export default function SubmitContainer(props) {
return (
<div className={style.submitContainer}>
<Button
type='submit'
isDisabled={submitting || !changed}
variant='ghosted'
onClick={() => revert()}