Compare commits

...

10 Commits

Author SHA1 Message Date
Ary 1bc6c3e852 feat: lots of renaming 2024-02-25 11:32:53 -07:00
Ary 257f2259e0 feat: starting to cleanup 2024-02-25 11:25:06 -07:00
Ary 8ae3bb2df0 feat: create works, just not ideal 2024-02-25 11:19:27 -07:00
Ary ff1496d29f feat: renaming 2024-02-24 13:53:06 -07:00
Ary 04455c08ef feat: some cleanup 2024-02-24 11:54:45 -07:00
Ary 6c86ece1ff feat: deletion 2024-02-24 11:47:30 -07:00
Ary 9a2fa527c6 feat: works, but still need adjustments 2024-02-24 11:42:04 -07:00
Ary 165920b9f2 feat: wip for form 2024-02-24 11:23:05 -07:00
Ary ee3b3c0735 feat: wip 2024-02-15 17:00:23 -07:00
Ary ecaf0a209b wip 2024-02-14 11:52:24 -07:00
22 changed files with 551 additions and 212 deletions
+18 -2
View File
@@ -73,13 +73,29 @@ export async function getAliases(): Promise<Alias[]> {
}
/**
* @description HTTP request to mutate aliases
* @description HTTP request to create an alias
* @return {Promise}
*/
export async function postAliases(data: Alias[]) {
export async function postAlias(data: Alias) {
return axios.post(`${ontimeURL}/aliases`, data);
}
/**
* @description HTTP request to update aliases
* @return {Promise}
*/
export async function updateAliases(data: Partial<Alias>) {
return axios.put(`${ontimeURL}/aliases`, data);
}
/**
* @description HTTP request to delete alias
* @return {Promise}
*/
export async function deleteAlias(alias: string) {
return axios.delete(`${ontimeURL}/aliases/${alias}`);
}
/**
* @description HTTP request to retrieve user fields
* @return {Promise}
@@ -7,6 +7,7 @@ import IntegrationsPanel from './panel/integrations-panel/IntegrationsPanel';
import LogPanel from './panel/log-panel/LogPanel';
import ProjectPanel from './panel/project-panel/ProjectPanel';
import ProjectSettingsPanel from './panel/project-settings-panel/ProjectSettingsPanel';
import UrlPresetPanel from './panel/url-preset-panel/UrlPresetPanel';
import PanelContent from './panel-content/PanelContent';
import PanelList from './panel-list/PanelList';
import { useSettingsStore } from './settingsStore';
@@ -30,6 +31,7 @@ export default function AppSettings() {
{selectedPanel === 'project' && <ProjectPanel />}
{selectedPanel === 'integrations' && <IntegrationsPanel />}
{selectedPanel === 'project_settings' && <ProjectSettingsPanel />}
{selectedPanel === 'url_presets' && <UrlPresetPanel />}
{selectedPanel === 'about' && <AboutPanel />}
{selectedPanel === 'log' && <LogPanel />}
</PanelContent>
@@ -0,0 +1,87 @@
import { useEffect } from 'react';
import { useForm } from 'react-hook-form';
import { Button, Input } from '@chakra-ui/react';
import style from './UrlPresetPanel.module.scss';
export type UrlPresetFormValues = {
alias?: string;
pathAndParams?: string;
enabled?: boolean;
};
interface UrlPresetFormProps {
alias?: string;
enabled?: boolean;
pathAndParams?: string;
onCancel: () => void;
onSubmit: (values: UrlPresetFormValues) => Promise<void>;
submitError: string | null;
}
export default function UrlPresetForm({
onSubmit,
onCancel,
submitError,
alias,
enabled,
pathAndParams,
}: UrlPresetFormProps) {
const {
handleSubmit,
register,
formState: { isSubmitting, isValid },
setFocus,
} = useForm<UrlPresetFormValues>({
defaultValues: { alias, pathAndParams, enabled },
values: { alias, pathAndParams, enabled },
resetOptions: {
keepDirtyValues: true,
},
});
useEffect(() => {
setFocus('alias');
}, [setFocus]);
return (
<form onSubmit={handleSubmit(onSubmit)} className={style.form}>
<div className={style.formInput}>
<Input
variant='ontime-filled'
size='sm'
maxLength={50}
placeholder='Alias'
autoComplete='off'
{...register('alias')}
/>
</div>
<div className={style.formInput}>
<Input
variant='ontime-filled'
size='sm'
maxLength={100}
placeholder='Path and params'
autoComplete='off'
{...register('pathAndParams')}
/>
</div>
<div className={style.actionButtons}>
<Button onClick={onCancel} variant='ontime-ghosted' size='sm'>
Cancel
</Button>
<Button
isDisabled={!isValid}
type='submit'
isLoading={isSubmitting}
variant='ontime-filled'
padding='0 2em'
size='sm'
>
Update
</Button>
</div>
{submitError && <span className={style.error}>{submitError}</span>}
</form>
);
}
@@ -0,0 +1,72 @@
import { postAlias } from '../../../../common/api/ontimeApi';
import useAliases from '../../../../common/hooks-query/useAliases';
import ModalLoader from '../../../modals/modal-loader/ModalLoader';
import * as Panel from '../PanelUtils';
import UrlPresetForm, { UrlPresetFormValues } from './UrlPresetForm';
import UrlPresetListItem from './UrlPresetListItem';
interface UrlPresetListProps {
isCreatingPresetURL: boolean;
onToggleCreate: () => void;
}
export default function UrlPresetList({ isCreatingPresetURL, onToggleCreate }: UrlPresetListProps) {
const { data, isFetching, refetch } = useAliases();
const handleRefetch = async () => {
await refetch();
};
const handleSubmitCreate = async (values: UrlPresetFormValues) => {
try {
// TODO: fix this
// @ts-ignore
await postAlias({
...values,
enabled: true,
});
await refetch();
} catch (error) {
// some error handling here
}
};
if (isFetching) {
return <ModalLoader />;
}
return (
<Panel.Table>
<thead>
<tr>
<th>Alias</th>
<th>Path and Params</th>
<th>Enabled</th>
<th />
</tr>
</thead>
<tbody>
{isCreatingPresetURL ? (
<tr>
<td colSpan={99}>
<UrlPresetForm onCancel={onToggleCreate} onSubmit={handleSubmitCreate} submitError='' />
{/* {submitError && <span className={style.createSubmitError}>{submitError}</span>} */}
</td>
</tr>
) : null}
{(data || []).map((alias) => {
return (
<UrlPresetListItem
alias={alias.alias}
enabled={alias.enabled}
pathAndParams={alias.pathAndParams}
onRefetch={handleRefetch}
key={alias.alias}
/>
);
})}
</tbody>
</Panel.Table>
);
}
@@ -0,0 +1,116 @@
import { useCallback, useMemo, useState } from 'react';
import { IconButton, Menu, MenuButton, MenuItem, MenuList, Switch } from '@chakra-ui/react';
import { IoEllipsisHorizontal } from '@react-icons/all-files/io5/IoEllipsisHorizontal';
import { deleteAlias, updateAliases } from '../../../../common/api/ontimeApi';
import UrlAliasForm, { UrlPresetFormValues } from './UrlPresetForm';
interface UrlPresetListItemProps {
alias: string;
enabled: boolean;
pathAndParams: string;
onRefetch: () => Promise<void>;
}
export default function UrlPresetListItem({ alias, enabled, pathAndParams, onRefetch }: UrlPresetListItemProps) {
const [isEditing, setIsEditing] = useState(false);
const handleToggle = useCallback(async () => {
// TODO: Error handling
await updateAliases({
alias,
enabled: !enabled,
pathAndParams,
});
await onRefetch();
}, [alias, enabled, onRefetch, pathAndParams]);
const handleToggleEditMode = useCallback(() => {
setIsEditing(!isEditing);
}, [isEditing]);
const handleSubmitUpdate = useCallback(
async (values: UrlPresetFormValues) => {
try {
await updateAliases(values);
await onRefetch();
} catch (error) {
// some error handling here
}
},
[onRefetch],
);
const handleDelete = useCallback(async () => {
await deleteAlias(alias);
await onRefetch();
}, [alias, onRefetch]);
const handleRenderAliases = useMemo(() => {
if (!isEditing) {
return (
<>
<td
style={{
width: '45%',
}}
>
{alias}
</td>
<td
style={{
width: '45%',
}}
>
{pathAndParams}
</td>
<td>
<Switch variant='ontime-on-light' isChecked={enabled} onChange={handleToggle} />
</td>
<td>
<ActionMenu onDelete={handleDelete} onChangeEditMode={handleToggleEditMode} />
</td>
</>
);
} else {
return (
<td colSpan={99}>
<UrlAliasForm
alias={alias}
enabled={enabled}
pathAndParams={pathAndParams}
onCancel={handleToggleEditMode}
onSubmit={handleSubmitUpdate}
submitError=''
/>
</td>
);
}
}, [alias, enabled, handleDelete, handleSubmitUpdate, handleToggle, handleToggleEditMode, isEditing, pathAndParams]);
return <tr key={alias}>{handleRenderAliases}</tr>;
}
function ActionMenu({ onChangeEditMode, onDelete }: { onChangeEditMode: () => void; onDelete: () => Promise<void> }) {
const handleEdit = () => {
onChangeEditMode();
};
return (
<Menu variant='ontime-on-dark' size='sm'>
<MenuButton
as={IconButton}
aria-label='Options'
icon={<IoEllipsisHorizontal />}
variant='ontime-ghosted'
size='sm'
/>
<MenuList>
<MenuItem onClick={handleEdit}>Edit</MenuItem>
<MenuItem onClick={onDelete}>Delete</MenuItem>
</MenuList>
</Menu>
);
}
@@ -0,0 +1,18 @@
.form {
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
}
.formInput {
flex: 2;
padding-right: 1rem;
}
.actionButtons {
display: flex;
align-items: center;
gap: 0.5rem;
margin-left: 1rem;
}
@@ -0,0 +1,56 @@
import { useState } from 'react';
import { Alert, AlertDescription, AlertIcon, AlertTitle, Button } from '@chakra-ui/react';
import ModalLink from '../../../modals/ModalLink';
import * as Panel from '../PanelUtils';
import UrlPresetList from './UrlPresetList';
import style from './UrlPresetPanel.module.scss';
const aliasesDocsUrl = 'https://ontime.gitbook.io/v2/features/url-aliases';
export default function UrlPresetPanel() {
const [isCreatingPresetURL, setIsCreatingPresetURL] = useState(false);
const handleToggleCreate = () => {
setIsCreatingPresetURL((prev) => !prev);
};
return (
<>
<Panel.Header>URL Presets</Panel.Header>
<Panel.Section>
<Panel.Card>
<main>
<Alert status='info' variant='ontime-on-light-info'>
<AlertIcon />
<div className={style.column}>
<AlertTitle>URL Presets</AlertTitle>
<AlertDescription>
Custom presets allow providing a short name for any ontime URL. <br />
It serves two primary purposes: <br />
- Providing dynamic URLs for automation or unattended screens <br />- Simplifying complex URLs
<ModalLink href={aliasesDocsUrl}>For more information, see the docs</ModalLink>
</AlertDescription>
</div>
</Alert>
<div
style={{
marginTop: '1rem',
}}
>
<Panel.SubHeader>
Manage URL presets
<Button variant='ontime-filled' onClick={handleToggleCreate}>
New
</Button>
</Panel.SubHeader>
</div>
<UrlPresetList isCreatingPresetURL={isCreatingPresetURL} onToggleCreate={handleToggleCreate} />
</main>
</Panel.Card>
</Panel.Section>
</>
);
}
@@ -35,6 +35,7 @@ export const settingPanels: Readonly<SettingsOption[]> = [
],
},
{ id: 'log', label: 'Log', split: true },
{ id: 'url_presets', label: 'URL Presets' },
{
id: 'about',
label: 'About',
@@ -1,178 +0,0 @@
import { useEffect } from 'react';
import { useFieldArray, useForm } from 'react-hook-form';
import { Alert, AlertDescription, AlertIcon, AlertTitle, Button, IconButton, Input, Switch } from '@chakra-ui/react';
import { IoOpenOutline } from '@react-icons/all-files/io5/IoOpenOutline';
import { IoRemove } from '@react-icons/all-files/io5/IoRemove';
import { Alias } from 'ontime-types';
import { logAxiosError } from '../../../common/api/apiUtils';
import { postAliases } from '../../../common/api/ontimeApi';
import TooltipActionBtn from '../../../common/components/buttons/TooltipActionBtn';
import useAliases from '../../../common/hooks-query/useAliases';
import { useEmitLog } from '../../../common/stores/logger';
import { handleLinks } from '../../../common/utils/linkUtils';
import ModalLoader from '../modal-loader/ModalLoader';
import { inputProps } from '../modalHelper';
import ModalLink from '../ModalLink';
import OntimeModalFooter from '../OntimeModalFooter';
import style from './SettingsModal.module.scss';
const aliasesDocsUrl = 'https://ontime.gitbook.io/v2/features/url-aliases';
// we wrap the array in an object to be simplify react-hook-form
type Aliases = {
aliases: Alias[];
};
export default function AliasesForm() {
const { data, status, isFetching, refetch } = useAliases();
const { emitError } = useEmitLog();
const {
control,
handleSubmit,
register,
reset,
formState: { isSubmitting, isDirty, isValid },
} = useForm<Aliases>({
defaultValues: { aliases: data },
values: { aliases: data || [] },
resetOptions: {
keepDirtyValues: true,
},
});
const { fields, append, remove } = useFieldArray({
name: 'aliases',
control,
});
useEffect(() => {
if (data) {
reset({ aliases: data });
}
}, [data, reset]);
const onSubmit = async (formData: Aliases) => {
try {
await postAliases(formData.aliases);
} catch (error) {
logAxiosError('Error saving aliases', error);
} finally {
await refetch();
}
};
const onReset = () => {
reset({ aliases: data });
};
const addNew = () => {
if (fields.length > 20) {
emitError('Maximum amount of aliases reached (20)');
return;
}
append({
enabled: false,
alias: '',
pathAndParams: '',
});
};
const disableInputs = status === 'pending';
const hasTooManyOptions = fields.length >= 20;
if (isFetching) {
return <ModalLoader />;
}
return (
<form onSubmit={handleSubmit(onSubmit)} id='aliases' className={style.sectionContainer}>
<div style={{ height: '16px' }} />
<Alert status='info' variant='ontime-on-light-info'>
<AlertIcon />
<div className={style.column}>
<AlertTitle>URL Aliases</AlertTitle>
<AlertDescription>
Custom aliases allow providing a short name for any ontime URL. <br />
It serves two primary purposes: <br />
- Providing dynamic URLs for automation or unattended screens <br />- Simplifying complex URLs
<ModalLink href={aliasesDocsUrl}>For more information, see the docs</ModalLink>
</AlertDescription>
</div>
</Alert>
<div style={{ height: '16px' }} />
<ul className={style.aliases}>
{fields.map((alias, index) => {
return (
<li className={style.aliasRow} key={alias.id}>
<IconButton
onClick={() => remove(index)}
aria-label='delete'
size='xs'
icon={<IoRemove />}
colorScheme='red'
isDisabled={disableInputs}
data-testid={`field__delete_${index}`}
/>
<Input
{...inputProps}
{...register(`aliases.${index}.alias`)}
width='12em'
size='xs'
variant='ontime-filled-on-light'
placeholder='URL Alias'
isDisabled={disableInputs}
data-testid={`field__alias_${index}`}
/>
<Input
{...inputProps}
{...register(`aliases.${index}.pathAndParams`)}
className={style.grow}
size='xs'
variant='ontime-filled-on-light'
placeholder='URL (portion after ontime Port)'
isDisabled={disableInputs}
data-testid={`field__url_${index}`}
/>
<TooltipActionBtn
clickHandler={(event) => handleLinks(event, alias.alias)}
tooltip='Test alias'
aria-label='Test alias'
size='xs'
variant='ontime-ghost-on-light'
icon={<IoOpenOutline />}
colorScheme='red'
isDisabled={disableInputs}
data-testid={`field__test_${index}`}
/>
<Switch
{...register(`aliases.${index}.enabled`)}
variant='ontime-on-light'
isDisabled={disableInputs}
data-testid={`field__enable_${index}`}
/>
</li>
);
})}
</ul>
<Button
onClick={addNew}
className={style.shiftRight}
isDisabled={hasTooManyOptions}
size='xs'
colorScheme='blue'
variant='outline'
padding='0 2em'
>
Add new
</Button>
<OntimeModalFooter
formId='aliases'
handleRevert={onReset}
isDirty={isDirty}
isValid={isValid}
isSubmitting={isSubmitting}
/>
</form>
);
}
@@ -2,7 +2,6 @@ import { ModalBody, Tab, TabList, TabPanel, TabPanels, Tabs } from '@chakra-ui/r
import ModalWrapper from '../ModalWrapper';
import AliasesForm from './AliasesForm';
import AppSettingsModal from './AppSettings';
import CuesheetSettingsForm from './CuesheetSettingsForm';
import EditorSettings from './EditorSettings';
@@ -26,7 +25,7 @@ export default function SettingsModal(props: ModalManagerProps) {
<Tab>Editor</Tab>
<Tab>Cuesheet</Tab>
<Tab>Views</Tab>
<Tab>URL Aliases</Tab>
<Tab>URL Presets</Tab>
</TabList>
<TabPanels>
<TabPanel>
@@ -44,9 +43,6 @@ export default function SettingsModal(props: ModalManagerProps) {
<TabPanel>
<ViewSettingsForm />
</TabPanel>
<TabPanel>
<AliasesForm />
</TabPanel>
</TabPanels>
</Tabs>
</ModalBody>
@@ -71,11 +71,17 @@ export class DataProvider {
return data.aliases;
}
// TODO: Remove this when I change the data model
static async setAliases(newData: Alias[]) {
data.aliases = newData;
await this.persist();
}
static async addAlias(newData: Alias) {
data.aliases.push(newData);
await this.persist();
}
static getUserFields() {
return { ...data.userFields };
}
+65 -19
View File
@@ -1,6 +1,5 @@
import { LogOrigin } from 'ontime-types';
import type {
Alias,
DatabaseModel,
GetInfo,
HttpSettings,
@@ -19,7 +18,7 @@ import { copyFile, rename, writeFile } from 'fs/promises';
import { fileHandler } from '../utils/parser.js';
import { DataProvider } from '../classes/data-provider/DataProvider.js';
import { failEmptyObjects, failIsNotArray } from '../utils/routerUtils.js';
import { failEmptyObjects } from '../utils/routerUtils.js';
import { runtimeService } from '../services/runtime-service/RuntimeService.js';
import { eventStore } from '../stores/EventStore.js';
import {
@@ -160,30 +159,77 @@ export const getInfo = async (_req: Request, res: Response<GetInfo>) => {
});
};
// Create controller for POST request to '/ontime/aliases'
// Create controller for POST request to '/ontime/presets'
// Returns -
export const getAliases = async (_req: Request, res: Response) => {
export const getPresets = async (_req: Request, res: Response) => {
const aliases = DataProvider.getAliases();
res.status(200).send(aliases);
};
// Create controller for POST request to '/ontime/aliases'
// Create controller for POST request to '/ontime/presets'
// Returns ACK message
export const postAliases = async (req: Request, res: Response) => {
if (failIsNotArray(req.body, res)) {
return;
}
export const postPreset = async (req: Request, res: Response) => {
try {
const newAliases: Alias[] = [];
req.body.forEach((a) => {
newAliases.push({
enabled: a.enabled,
alias: a.alias,
pathAndParams: a.pathAndParams,
});
});
await DataProvider.setAliases(newAliases);
res.status(200).send(newAliases);
const newAlias = {
enabled: req.body.enabled,
alias: req.body.alias,
pathAndParams: req.body.pathAndParams,
};
await DataProvider.addAlias(newAlias);
res.status(200).send(newAlias);
} catch (error) {
res.status(400).send({ message: String(error) });
}
};
// Update controller for PUT request to '/ontime/presets/:preset'
// Returns ACK message
export const updatePreset = async (req: Request, res: Response) => {
try {
const { alias, enabled, pathAndParams } = req.body;
const aliases = DataProvider.getAliases();
const index = aliases.findIndex((a) => a.alias === alias);
if (index === -1) {
return res.status(404).send({ message: `Alias ${alias} not found` });
}
aliases[index] = {
alias,
enabled,
pathAndParams,
};
await DataProvider.setAliases(aliases);
res.status(200).send(aliases);
} catch (error) {
res.status(400).send({ message: String(error) });
}
};
// Delete controller for DELETE request to '/ontime/presets/:preset'
// Returns ACK message
export const deletePreset = async (req: Request, res: Response) => {
try {
const alias = req.params.alias;
const aliases = DataProvider.getAliases();
const index = aliases.findIndex((a) => a.alias === alias);
if (index === -1) {
return res.status(404).send({ message: `Alias ${alias} not found` });
}
aliases.splice(index, 1);
await DataProvider.setAliases(aliases);
res.status(200).send(aliases);
} catch (error) {
res.status(400).send({ message: String(error) });
}
@@ -24,9 +24,9 @@ export const viewValidator = [
];
/**
* @description Validates object for POST /ontime/aliases
* @description Validates object for POST /ontime/presets
*/
export const validateAliases = [
export const validatePresets = [
body().isArray(),
body('*.enabled').isBoolean(),
body('*.alias').isString().trim(),
@@ -39,6 +39,36 @@ export const validateAliases = [
},
];
/**
* @description Validates object for POST /ontime/presets
*/
export const validateCreatePreset = [
body('alias').isString().trim(),
body('pathAndParams').isString().trim(),
body('enabled').isBoolean(),
(req: Request, res: Response, next: NextFunction) => {
const errors = validationResult(req);
if (!errors.isEmpty()) return res.status(422).json({ errors: errors.array() });
next();
},
];
/**
* @description Validates object for PUT /ontime/presets
*/
export const validateUpdatePreset = [
body('alias').isString().trim(),
body('pathAndParams').isString().trim().optional(),
body('enabled').isBoolean().optional(),
(req: Request, res: Response, next: NextFunction) => {
const errors = validationResult(req);
if (!errors.isEmpty()) return res.status(422).json({ errors: errors.array() });
next();
},
];
/**
* @description Validates object for POST /ontime/userfields
*/
@@ -121,7 +151,7 @@ export const validatePatchProjectFile = [
body('project').isObject().optional({ nullable: false }),
body('settings').isObject().optional({ nullable: false }),
body('viewSettings').isObject().optional({ nullable: false }),
body('aliases').isArray().optional({ nullable: false }),
body('aliases').isArray().optional({ nullable: false }), // TODO
body('userFields').isObject().optional({ nullable: false }),
body('osc').isObject().optional({ nullable: false }),
+14 -5
View File
@@ -3,7 +3,6 @@ import { uploadFile } from '../utils/upload.js';
import {
dbDownload,
dbUpload,
getAliases,
getInfo,
getOSC,
getHTTP,
@@ -12,7 +11,6 @@ import {
getViewSettings,
patchPartialProjectFile,
poll,
postAliases,
postOSC,
postSettings,
postUserFields,
@@ -32,10 +30,13 @@ import {
postId,
getAuthentication,
getClientSecrect as getClientSecret,
postPreset,
updatePreset,
deletePreset,
getPresets,
} from '../controllers/ontimeController.js';
import {
validateAliases,
validateOSC,
validatePatchProjectFile,
validateSettings,
@@ -48,6 +49,8 @@ import {
validateSheetid,
validateWorksheet,
validateSheetOptions,
validateCreatePreset,
validateUpdatePreset,
} from '../controllers/ontimeController.validate.js';
import { projectSanitiser } from '../controllers/projectController.validate.js';
import { sanitizeProjectFilename } from '../utils/sanitizeProjectFilename.js';
@@ -82,10 +85,16 @@ router.get('/views', getViewSettings);
router.post('/views', viewValidator, postViewSettings);
// create route between controller and '/ontime/aliases' endpoint
router.get('/aliases', getAliases);
router.get('/presets', getPresets);
// create route between controller and '/ontime/aliases' endpoint
router.post('/aliases', validateAliases, postAliases);
router.post('/presets', validateCreatePreset, postPreset);
// create route between controller and /ontime/aliases' endpoint
router.put('/presets', validateUpdatePreset, updatePreset);
// create route between controller and '/ontime/aliases' endpoint
router.delete('/presets/:preset', deletePreset);
// create route between controller and '/ontime/aliases' endpoint
router.get('/userfields', getUserFields);
+2
View File
@@ -37,6 +37,7 @@ import {
parseSettings,
parseUserFields,
parseViewSettings,
parsePresets,
} from './parserFunctions.js';
import { parseExcelDate } from './time.js';
import { configService } from '../services/ConfigService.js';
@@ -329,6 +330,7 @@ export const parseJson = async (jsonData: Partial<DatabaseModel>): Promise<Datab
settings: parseSettings(jsonData) ?? dbModel.settings,
viewSettings: parseViewSettings(jsonData) ?? dbModel.viewSettings,
aliases: parseAliases(jsonData),
presets: parsePresets(jsonData),
userFields: parseUserFields(jsonData),
osc: parseOsc(jsonData) ?? dbModel.osc,
http: parseHttp(jsonData) ?? dbModel.http,
+27
View File
@@ -15,6 +15,7 @@ import {
isOntimeBlock,
isOntimeCycle,
HttpSubscription,
Presets,
} from 'ontime-types';
import { block as blockDef, delay as delayDef } from '../models/eventsDefinition.js';
@@ -250,6 +251,32 @@ export const parseAliases = (data): Alias[] => {
return newAliases;
};
/**
* Parse presets portion of an entry
* @param {object} data - data object
* @returns {object} - event object data
*/
export const parsePresets = (data): Presets => {
const newPresets: { [key: string]: { alias: string; pathAndParams: string; enabled: boolean } } = {};
if ('presets' in data) {
console.log('Found Presets definition, importing...');
try {
for (const preset in data.presets) {
const newPreset = {
alias: data.presets[preset].alias,
pathAndParams: data.presets[preset].pathAndParams,
enabled: data.presets[preset].enabled,
};
newPresets[preset] = newPreset;
}
console.log(`Uploaded ${Object.keys(newPresets).length} preset(s)`);
} catch (error) {
console.log(`Error: ${error}`);
}
}
return newPresets;
};
/**
* Parse userFields entry
* @param {object} data - data object
+7
View File
@@ -436,6 +436,13 @@
"pathAndParams": "lower?bg=ff2&text=f00&size=0.6&transition=5"
}
],
"presets": {
"test": {
"enabled": true,
"preset": "test",
"pathAndParams": "lower?bg=ff2&text=f00&size=0.6&transition=5"
}
},
"userFields": {
"user0": "user0",
"user1": "user1",
+7
View File
@@ -436,6 +436,13 @@
"pathAndParams": "lower?bg=ff2&text=f00&size=0.6&transition=5"
}
],
"presets": {
"test": {
"enabled": true,
"preset": "test",
"pathAndParams": "lower?bg=ff2&text=f00&size=0.6&transition=5"
}
},
"userFields": {
"user0": "user0",
"user1": "user1",
+7
View File
@@ -436,6 +436,13 @@
"pathAndParams": "lower?bg=ff2&text=f00&size=0.6&transition=5"
}
],
"presets": {
"test": {
"enabled": true,
"preset": "test",
"pathAndParams": "lower?bg=ff2&text=f00&size=0.6&transition=5"
}
},
"userFields": {
"user0": "user0",
"user1": "user1",
@@ -6,6 +6,7 @@ import { Settings } from './core/Settings.type.js';
import { UserFields } from './core/UserFields.type.js';
import { ViewSettings } from './core/Views.type.js';
import { HttpSettings } from '../index.js';
import { Presets } from './core/Presets.type.js';
export type DatabaseModel = {
rundown: OntimeRundown;
@@ -13,6 +14,7 @@ export type DatabaseModel = {
settings: Settings;
viewSettings: ViewSettings;
aliases: Alias[];
presets: Presets;
userFields: UserFields;
osc: OSCSettings;
http: HttpSettings;
@@ -0,0 +1,7 @@
export type Presets = {
[key: string]: {
alias: string;
pathAndParams: string;
enabled: boolean;
};
};
+3
View File
@@ -27,6 +27,9 @@ export type { TimeFormat } from './definitions/core/TimeFormat.type.js';
// ---> Aliases
export type { Alias } from './definitions/core/Alias.type.js';
// ---> Presets
export type { Presets } from './definitions/core/Presets.type.js';
// ---> User Fields
export type { UserFields } from './definitions/core/UserFields.type.js';