mirror of
https://github.com/cpvalente/ontime.git
synced 2026-09-05 14:29:20 +00:00
feat(url-presets): allow presets to appear as nav menu items (#2110)
This commit is contained in:
@@ -8,6 +8,7 @@ import { useLocation } from 'react-router';
|
|||||||
import { isLocalhost, supportsFullscreen } from '../../../externals';
|
import { isLocalhost, supportsFullscreen } from '../../../externals';
|
||||||
import { canUseWakeLock, useKeepAwakeOptions } from '../../../features/keep-awake/useWakeLock';
|
import { canUseWakeLock, useKeepAwakeOptions } from '../../../features/keep-awake/useWakeLock';
|
||||||
import { navigatorConstants } from '../../../viewerConfig';
|
import { navigatorConstants } from '../../../viewerConfig';
|
||||||
|
import useUrlPresets from '../../hooks-query/useUrlPresets';
|
||||||
import { useIsSmallScreen } from '../../hooks/useIsSmallScreen';
|
import { useIsSmallScreen } from '../../hooks/useIsSmallScreen';
|
||||||
import { useClientStore } from '../../stores/clientStore';
|
import { useClientStore } from '../../stores/clientStore';
|
||||||
import { useViewOptionsStore } from '../../stores/viewOptions';
|
import { useViewOptionsStore } from '../../stores/viewOptions';
|
||||||
@@ -105,6 +106,8 @@ function NavigationMenu({ isOpen, onClose }: NavigationMenuProps) {
|
|||||||
{route.label}
|
{route.label}
|
||||||
</ClientLink>
|
</ClientLink>
|
||||||
))}
|
))}
|
||||||
|
|
||||||
|
<PresetNavigation isSmallScreen={isSmallScreen} onClose={onClose} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{isLocalhost && (
|
{isLocalhost && (
|
||||||
@@ -117,3 +120,27 @@ function NavigationMenu({ isOpen, onClose }: NavigationMenuProps) {
|
|||||||
</Dialog.Root>
|
</Dialog.Root>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function PresetNavigation({ isSmallScreen, onClose }: { isSmallScreen: boolean; onClose: () => void }) {
|
||||||
|
const location = useLocation();
|
||||||
|
const { data: urlPresets } = useUrlPresets();
|
||||||
|
const navPresets = urlPresets.filter((preset) => preset.enabled && preset.displayInNav);
|
||||||
|
|
||||||
|
if (navPresets.length === 0) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<hr className={style.separator} />
|
||||||
|
{navPresets.map((preset) => (
|
||||||
|
<ClientLink
|
||||||
|
key={preset.alias}
|
||||||
|
to={`preset/${preset.alias}`}
|
||||||
|
current={location.pathname === `/preset/${preset.alias}`}
|
||||||
|
postAction={isSmallScreen ? onClose : undefined}
|
||||||
|
>
|
||||||
|
{preset.alias}
|
||||||
|
</ClientLink>
|
||||||
|
))}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ describe('getRouteFromPreset()', () => {
|
|||||||
alias: 'demopage',
|
alias: 'demopage',
|
||||||
target: OntimeView.Timer,
|
target: OntimeView.Timer,
|
||||||
search: 'user=guest',
|
search: 'user=guest',
|
||||||
|
displayInNav: false,
|
||||||
options: {},
|
options: {},
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
@@ -119,6 +120,7 @@ describe('getRouteFromPreset()', () => {
|
|||||||
alias: 'cuesheet-4685d6',
|
alias: 'cuesheet-4685d6',
|
||||||
target: OntimeView.Cuesheet,
|
target: OntimeView.Cuesheet,
|
||||||
search: '',
|
search: '',
|
||||||
|
displayInNav: false,
|
||||||
options: {
|
options: {
|
||||||
read: 'full',
|
read: 'full',
|
||||||
write: '-',
|
write: '-',
|
||||||
@@ -131,6 +133,7 @@ describe('getRouteFromPreset()', () => {
|
|||||||
alias: 'cuesheet-basic',
|
alias: 'cuesheet-basic',
|
||||||
target: OntimeView.Cuesheet,
|
target: OntimeView.Cuesheet,
|
||||||
search: '',
|
search: '',
|
||||||
|
displayInNav: false,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
const cuesheetPresetWithNavLock: URLPreset[] = [
|
const cuesheetPresetWithNavLock: URLPreset[] = [
|
||||||
@@ -139,6 +142,7 @@ describe('getRouteFromPreset()', () => {
|
|||||||
alias: 'cuesheet-locked',
|
alias: 'cuesheet-locked',
|
||||||
target: OntimeView.Cuesheet,
|
target: OntimeView.Cuesheet,
|
||||||
search: 'n=1',
|
search: 'n=1',
|
||||||
|
displayInNav: false,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -220,6 +224,7 @@ describe('generateUrlPresetOptions', () => {
|
|||||||
target: 'timer',
|
target: 'timer',
|
||||||
search: 'param1=value1¶m2=value2',
|
search: 'param1=value1¶m2=value2',
|
||||||
enabled: true,
|
enabled: true,
|
||||||
|
displayInNav: false,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
@@ -231,6 +236,7 @@ describe('generateUrlPresetOptions', () => {
|
|||||||
target: 'timer',
|
target: 'timer',
|
||||||
search: 'param1=value1¶m2=value2',
|
search: 'param1=value1¶m2=value2',
|
||||||
enabled: true,
|
enabled: true,
|
||||||
|
displayInNav: false,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
@@ -242,6 +248,7 @@ describe('generateUrlPresetOptions', () => {
|
|||||||
target: 'timer',
|
target: 'timer',
|
||||||
search: 'param1=value1¶m2=value2',
|
search: 'param1=value1¶m2=value2',
|
||||||
enabled: true,
|
enabled: true,
|
||||||
|
displayInNav: false,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
@@ -253,6 +260,7 @@ describe('generateUrlPresetOptions', () => {
|
|||||||
target: 'timer',
|
target: 'timer',
|
||||||
search: 'param1=value1¶m2=value2',
|
search: 'param1=value1¶m2=value2',
|
||||||
enabled: true,
|
enabled: true,
|
||||||
|
displayInNav: false,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
])('should generate URL preset options for %s', (_description, alias, url, expected) => {
|
])('should generate URL preset options for %s', (_description, alias, url, expected) => {
|
||||||
|
|||||||
@@ -194,6 +194,7 @@ export function generateUrlPresetOptions(alias: string, userUrl: string): URLPre
|
|||||||
target: path,
|
target: path,
|
||||||
search: url.searchParams.toString(),
|
search: url.searchParams.toString(),
|
||||||
enabled: true,
|
enabled: true,
|
||||||
|
displayInNav: false,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { URLPreset } from 'ontime-types';
|
|||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { IoAdd, IoOpenOutline, IoPencil, IoTrash } from 'react-icons/io5';
|
import { IoAdd, IoOpenOutline, IoPencil, IoTrash } from 'react-icons/io5';
|
||||||
|
|
||||||
|
import { maybeAxiosError } from '../../../../common/api/utils';
|
||||||
import Button from '../../../../common/components/buttons/Button';
|
import Button from '../../../../common/components/buttons/Button';
|
||||||
import IconButton from '../../../../common/components/buttons/IconButton';
|
import IconButton from '../../../../common/components/buttons/IconButton';
|
||||||
import Info from '../../../../common/components/info/Info';
|
import Info from '../../../../common/components/info/Info';
|
||||||
@@ -22,13 +23,23 @@ const urlPresetsDocs = 'https://docs.getontime.no/features/url-presets/';
|
|||||||
|
|
||||||
export default function URLPresets() {
|
export default function URLPresets() {
|
||||||
const [formState, setFormState] = useState<FormState>({ isOpen: false, preset: undefined });
|
const [formState, setFormState] = useState<FormState>({ isOpen: false, preset: undefined });
|
||||||
|
const [actionError, setActionError] = useState<string | null>(null);
|
||||||
const { data, status } = useUrlPresets();
|
const { data, status } = useUrlPresets();
|
||||||
const { deletePreset, isMutating } = useUpdateUrlPreset();
|
const { updatePreset, deletePreset, isMutating } = useUpdateUrlPreset();
|
||||||
|
|
||||||
const openNewForm = () => setFormState({ isOpen: true });
|
const openNewForm = () => setFormState({ isOpen: true });
|
||||||
const openEditForm = (preset: URLPreset) => setFormState({ isOpen: true, preset });
|
const openEditForm = (preset: URLPreset) => setFormState({ isOpen: true, preset });
|
||||||
const closeForm = () => setFormState({ isOpen: false, preset: undefined });
|
const closeForm = () => setFormState({ isOpen: false, preset: undefined });
|
||||||
|
|
||||||
|
const persistPreset = async (preset: URLPreset) => {
|
||||||
|
setActionError(null);
|
||||||
|
try {
|
||||||
|
await updatePreset(preset.alias, preset);
|
||||||
|
} catch (error) {
|
||||||
|
setActionError(maybeAxiosError(error));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Panel.Section>
|
<Panel.Section>
|
||||||
<Panel.Card>
|
<Panel.Card>
|
||||||
@@ -53,10 +64,12 @@ export default function URLPresets() {
|
|||||||
<Panel.Section>
|
<Panel.Section>
|
||||||
<Panel.Loader isLoading={status === 'pending'} />
|
<Panel.Loader isLoading={status === 'pending'} />
|
||||||
{formState.isOpen && <URLPresetForm urlPreset={formState.preset} onClose={closeForm} />}
|
{formState.isOpen && <URLPresetForm urlPreset={formState.preset} onClose={closeForm} />}
|
||||||
|
{actionError && <Panel.Error>{actionError}</Panel.Error>}
|
||||||
<Panel.Table>
|
<Panel.Table>
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Enabled</th>
|
<th>Enabled</th>
|
||||||
|
<th>Show in nav</th>
|
||||||
<th>Target view</th>
|
<th>Target view</th>
|
||||||
<th>Alias</th>
|
<th>Alias</th>
|
||||||
<th />
|
<th />
|
||||||
@@ -68,7 +81,18 @@ export default function URLPresets() {
|
|||||||
return (
|
return (
|
||||||
<tr key={preset.alias}>
|
<tr key={preset.alias}>
|
||||||
<td>
|
<td>
|
||||||
<Switch defaultChecked={preset.enabled} onCheckedChange={() => {}} />
|
<Switch
|
||||||
|
checked={preset.enabled}
|
||||||
|
onCheckedChange={(checked) => persistPreset({ ...preset, enabled: checked })}
|
||||||
|
disabled={isMutating}
|
||||||
|
/>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<Switch
|
||||||
|
checked={preset.displayInNav}
|
||||||
|
onCheckedChange={(checked) => persistPreset({ ...preset, displayInNav: checked })}
|
||||||
|
disabled={isMutating}
|
||||||
|
/>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<Tag>{preset.target}</Tag>
|
<Tag>{preset.target}</Tag>
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ const defaultValues: URLPreset = {
|
|||||||
target: OntimeView.Timer,
|
target: OntimeView.Timer,
|
||||||
search: '',
|
search: '',
|
||||||
enabled: true,
|
enabled: true,
|
||||||
|
displayInNav: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
interface URLPresetFormProps {
|
interface URLPresetFormProps {
|
||||||
|
|||||||
@@ -95,6 +95,7 @@ export default function GenerateLinkForm({ hostOptions, pathOptions, presets, is
|
|||||||
enabled: true,
|
enabled: true,
|
||||||
alias,
|
alias,
|
||||||
search: '',
|
search: '',
|
||||||
|
displayInNav: false,
|
||||||
options: {
|
options: {
|
||||||
read: options.read,
|
read: options.read,
|
||||||
write: options.write,
|
write: options.write,
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ describe('getCuesheetPermissionsPolicy()', () => {
|
|||||||
alias: 'cuesheet-read-only',
|
alias: 'cuesheet-read-only',
|
||||||
target: OntimeView.Cuesheet,
|
target: OntimeView.Cuesheet,
|
||||||
search: '',
|
search: '',
|
||||||
|
displayInNav: false,
|
||||||
options: {
|
options: {
|
||||||
read: 'full',
|
read: 'full',
|
||||||
write: '-',
|
write: '-',
|
||||||
@@ -41,6 +42,7 @@ describe('getCuesheetPermissionsPolicy()', () => {
|
|||||||
alias: 'cuesheet-flag',
|
alias: 'cuesheet-flag',
|
||||||
target: OntimeView.Cuesheet,
|
target: OntimeView.Cuesheet,
|
||||||
search: '',
|
search: '',
|
||||||
|
displayInNav: false,
|
||||||
options: {
|
options: {
|
||||||
read: 'full',
|
read: 'full',
|
||||||
write: 'flag',
|
write: 'flag',
|
||||||
@@ -62,6 +64,7 @@ describe('getCuesheetPermissionsPolicy()', () => {
|
|||||||
alias: 'cuesheet-default',
|
alias: 'cuesheet-default',
|
||||||
target: OntimeView.Cuesheet,
|
target: OntimeView.Cuesheet,
|
||||||
search: '',
|
search: '',
|
||||||
|
displayInNav: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
const policy = getCuesheetColumnAccessPolicy(preset, AppMode.Edit);
|
const policy = getCuesheetColumnAccessPolicy(preset, AppMode.Edit);
|
||||||
@@ -76,6 +79,7 @@ describe('getCuesheetPermissionsPolicy()', () => {
|
|||||||
alias: 'cuesheet-granular',
|
alias: 'cuesheet-granular',
|
||||||
target: OntimeView.Cuesheet,
|
target: OntimeView.Cuesheet,
|
||||||
search: '',
|
search: '',
|
||||||
|
displayInNav: false,
|
||||||
options: {
|
options: {
|
||||||
read: 'cue,title',
|
read: 'cue,title',
|
||||||
write: 'title',
|
write: 'title',
|
||||||
|
|||||||
@@ -110,14 +110,13 @@ type old_URLPreset = {
|
|||||||
/**
|
/**
|
||||||
* migrates a url presets from v3 to v4
|
* migrates a url presets from v3 to v4
|
||||||
* - pathAndParams split into a target and search
|
* - pathAndParams split into a target and search
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
export function migrateURLPresets(jsonData: object): URLPreset[] | undefined {
|
export function migrateURLPresets(jsonData: object): URLPreset[] | undefined {
|
||||||
if (is.objectWithKeys(jsonData, ['urlPresets']) && is.array(jsonData.urlPresets)) {
|
if (is.objectWithKeys(jsonData, ['urlPresets']) && is.array(jsonData.urlPresets)) {
|
||||||
const oldURLPresets = structuredClone(jsonData.urlPresets) as old_URLPreset;
|
const oldURLPresets = structuredClone(jsonData.urlPresets) as old_URLPreset;
|
||||||
const newURLPreset: URLPreset[] = oldURLPresets.map(({ enabled, alias, pathAndParams }) => {
|
const newURLPreset: URLPreset[] = oldURLPresets.map(({ enabled, alias, pathAndParams }) => {
|
||||||
const [target, search] = pathAndParams.split('?');
|
const [target, search] = pathAndParams.split('?');
|
||||||
return { enabled, alias, target, search, options: {} } as URLPreset;
|
return { enabled, alias, target, search, displayInNav: false, options: {} } as URLPreset;
|
||||||
});
|
});
|
||||||
return newURLPreset;
|
return newURLPreset;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -208,6 +208,7 @@ describe('v3 to v4', () => {
|
|||||||
target: OntimeView.Timer,
|
target: OntimeView.Timer,
|
||||||
search:
|
search:
|
||||||
'showLeadingZeros=true&timerType=clock&hideClock=true&hideCards=true&hideProgress=true&hideMessage=true&hideSecondary=true&hideLogo=true',
|
'showLeadingZeros=true&timerType=clock&hideClock=true&hideCards=true&hideProgress=true&hideMessage=true&hideSecondary=true&hideLogo=true',
|
||||||
|
displayInNav: false,
|
||||||
options: {},
|
options: {},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -216,6 +217,7 @@ describe('v3 to v4', () => {
|
|||||||
target: OntimeView.Timer,
|
target: OntimeView.Timer,
|
||||||
search:
|
search:
|
||||||
'showLeadingZeros=true&hideClock=true&hideCards=true&hideProgress=true&hideMessage=true&hideSecondary=true&hideLogo=true',
|
'showLeadingZeros=true&hideClock=true&hideCards=true&hideProgress=true&hideMessage=true&hideSecondary=true&hideLogo=true',
|
||||||
|
displayInNav: false,
|
||||||
options: {},
|
options: {},
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -12,7 +12,9 @@ describe('parseUrlPresets()', () => {
|
|||||||
|
|
||||||
it('parses data, skipping invalid results', () => {
|
it('parses data, skipping invalid results', () => {
|
||||||
const errorEmitter = vi.fn();
|
const errorEmitter = vi.fn();
|
||||||
const urlPresets = [{ enabled: true, alias: 'alias', target: 'timer', search: 'ss' }] as URLPreset[];
|
const urlPresets = [
|
||||||
|
{ enabled: true, alias: 'alias', target: 'timer', search: 'ss', displayInNav: false },
|
||||||
|
] as URLPreset[];
|
||||||
const result = parseUrlPresets({ urlPresets }, errorEmitter);
|
const result = parseUrlPresets({ urlPresets }, errorEmitter);
|
||||||
expect(result.length).toEqual(1);
|
expect(result.length).toEqual(1);
|
||||||
expect(result.at(0)).toMatchObject({
|
expect(result.at(0)).toMatchObject({
|
||||||
@@ -36,6 +38,7 @@ describe('parseUrlPresets()', () => {
|
|||||||
alias: 'testalias',
|
alias: 'testalias',
|
||||||
target: 'timer',
|
target: 'timer',
|
||||||
search: 'testpathAndParams',
|
search: 'testpathAndParams',
|
||||||
|
displayInNav: false,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
} as unknown as DatabaseModel;
|
} as unknown as DatabaseModel;
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ export function parseUrlPresets(data: Partial<DatabaseModel>, emitError?: ErrorE
|
|||||||
alias: preset.alias,
|
alias: preset.alias,
|
||||||
target: preset.target,
|
target: preset.target,
|
||||||
search: preset.search ?? '',
|
search: preset.search ?? '',
|
||||||
|
displayInNav: preset.displayInNav ?? false,
|
||||||
options: preset?.options,
|
options: preset?.options,
|
||||||
};
|
};
|
||||||
newPresets.push(newPreset);
|
newPresets.push(newPreset);
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ router.post('/', validateNewPreset, async (req: Request, res: Response<URLPreset
|
|||||||
alias: req.body.alias,
|
alias: req.body.alias,
|
||||||
target: req.body.target,
|
target: req.body.target,
|
||||||
search: req.body.search,
|
search: req.body.search,
|
||||||
|
displayInNav: req.body.displayInNav,
|
||||||
options: req.body.options,
|
options: req.body.options,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -55,6 +56,7 @@ router.put('/:alias', validateUpdatePreset, async (req: Request, res: Response<U
|
|||||||
alias: req.body.alias,
|
alias: req.body.alias,
|
||||||
target: req.body.target,
|
target: req.body.target,
|
||||||
search: req.body.search,
|
search: req.body.search,
|
||||||
|
displayInNav: req.body.displayInNav,
|
||||||
options: req.body.options ?? existingPreset.options,
|
options: req.body.options ?? existingPreset.options,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ export const validateNewPreset = [
|
|||||||
body('alias').isString().trim().notEmpty(),
|
body('alias').isString().trim().notEmpty(),
|
||||||
body('target').isString().trim().notEmpty().isIn(Object.values(OntimeView)),
|
body('target').isString().trim().notEmpty().isIn(Object.values(OntimeView)),
|
||||||
body('search').isString().trim(),
|
body('search').isString().trim(),
|
||||||
|
body('displayInNav').isBoolean(),
|
||||||
|
|
||||||
// options are currently only provided for cuesheet presets
|
// options are currently only provided for cuesheet presets
|
||||||
body('options').optional().isObject(),
|
body('options').optional().isObject(),
|
||||||
@@ -27,6 +28,7 @@ export const validateUpdatePreset = [
|
|||||||
body('alias').isString().trim().notEmpty(),
|
body('alias').isString().trim().notEmpty(),
|
||||||
body('target').isString().trim().notEmpty().isIn(Object.values(OntimeView)),
|
body('target').isString().trim().notEmpty().isIn(Object.values(OntimeView)),
|
||||||
body('search').isString().trim(),
|
body('search').isString().trim(),
|
||||||
|
body('displayInNav').isBoolean(),
|
||||||
|
|
||||||
// options are currently only provided for cuesheet presets
|
// options are currently only provided for cuesheet presets
|
||||||
body('options').optional().isObject(),
|
body('options').optional().isObject(),
|
||||||
|
|||||||
@@ -87,8 +87,8 @@ describe('safeMerge', () => {
|
|||||||
it('should merge the urlPresets key when present', () => {
|
it('should merge the urlPresets key when present', () => {
|
||||||
const newData = {
|
const newData = {
|
||||||
urlPresets: [
|
urlPresets: [
|
||||||
{ enabled: true, alias: 'alias1', search: '' },
|
{ enabled: true, alias: 'alias1', target: 'timer', search: '', displayInNav: false },
|
||||||
{ enabled: true, alias: 'alias2', search: '' },
|
{ enabled: true, alias: 'alias2', target: 'timer', search: '', displayInNav: false },
|
||||||
] as URLPreset[],
|
] as URLPreset[],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ export const demoDb: DatabaseModel = {
|
|||||||
target: OntimeView.Timer,
|
target: OntimeView.Timer,
|
||||||
search:
|
search:
|
||||||
'showLeadingZeros=true&timerType=clock&hideClock=true&hideCards=true&hideProgress=true&hideMessage=true&hideSecondary=true&hideLogo=true',
|
'showLeadingZeros=true&timerType=clock&hideClock=true&hideCards=true&hideProgress=true&hideMessage=true&hideSecondary=true&hideLogo=true',
|
||||||
|
displayInNav: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
enabled: true,
|
enabled: true,
|
||||||
@@ -50,6 +51,7 @@ export const demoDb: DatabaseModel = {
|
|||||||
target: OntimeView.Timer,
|
target: OntimeView.Timer,
|
||||||
search:
|
search:
|
||||||
'hideclock=true&hidecards=true&hideprogress=true&hidemessage=true&hidesecondary=true&hidelogo=true&font=arial+black&keycolour=00ff00&timerColour=ffffff',
|
'hideclock=true&hidecards=true&hideprogress=true&hidemessage=true&hidesecondary=true&hidelogo=true&font=arial+black&keycolour=00ff00&timerColour=ffffff',
|
||||||
|
displayInNav: false,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
customFields: {
|
customFields: {
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ type BaseURLPreset = {
|
|||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
alias: string;
|
alias: string;
|
||||||
search: string;
|
search: string;
|
||||||
|
displayInNav: boolean;
|
||||||
options?: Record<string, string>;
|
options?: Record<string, string>;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -28,6 +29,7 @@ type CuesheetUrlPreset = {
|
|||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
alias: string;
|
alias: string;
|
||||||
search: string;
|
search: string;
|
||||||
|
displayInNav: boolean;
|
||||||
options?: {
|
options?: {
|
||||||
read?: string;
|
read?: string;
|
||||||
write?: string;
|
write?: string;
|
||||||
|
|||||||
Reference in New Issue
Block a user