mirror of
https://github.com/cpvalente/ontime.git
synced 2026-08-27 09:59:08 +00:00
Adding Alias as a Parameter (#471)
* redirect on alias to a new page * move alias to a HOC wrapper
This commit is contained in:
@@ -1,8 +1,7 @@
|
|||||||
import { lazy, Suspense, useEffect } from 'react';
|
import { lazy, Suspense } from 'react';
|
||||||
import { Navigate, Route, Routes, useLocation, useNavigate } from 'react-router-dom';
|
import { Navigate, Route, Routes } from 'react-router-dom';
|
||||||
|
|
||||||
import useAliases from './common/hooks-query/useAliases';
|
|
||||||
import withData from './features/viewers/ViewWrapper';
|
import withData from './features/viewers/ViewWrapper';
|
||||||
|
import withAlias from './features/AliasWrapper';
|
||||||
|
|
||||||
const Editor = lazy(() => import('./features/editors/ProtectedEditor'));
|
const Editor = lazy(() => import('./features/editors/ProtectedEditor'));
|
||||||
const Cuesheet = lazy(() => import('./features/cuesheet/ProtectedCuesheet'));
|
const Cuesheet = lazy(() => import('./features/cuesheet/ProtectedCuesheet'));
|
||||||
@@ -17,14 +16,14 @@ const Public = lazy(() => import('./features/viewers/public/Public'));
|
|||||||
const Lower = lazy(() => import('./features/viewers/lower-thirds/LowerWrapper'));
|
const Lower = lazy(() => import('./features/viewers/lower-thirds/LowerWrapper'));
|
||||||
const StudioClock = lazy(() => import('./features/viewers/studio/StudioClock'));
|
const StudioClock = lazy(() => import('./features/viewers/studio/StudioClock'));
|
||||||
|
|
||||||
const STimer = withData(TimerView);
|
const STimer = withAlias(withData(TimerView));
|
||||||
const SMinimalTimer = withData(MinimalTimerView);
|
const SMinimalTimer = withAlias(withData(MinimalTimerView));
|
||||||
const SClock = withData(ClockView);
|
const SClock = withAlias(withData(ClockView));
|
||||||
const SCountdown = withData(Countdown);
|
const SCountdown = withAlias(withData(Countdown));
|
||||||
const SBackstage = withData(Backstage);
|
const SBackstage = withAlias(withData(Backstage));
|
||||||
const SPublic = withData(Public);
|
const SPublic = withAlias(withData(Public));
|
||||||
const SLowerThird = withData(Lower);
|
const SLowerThird = withAlias(withData(Lower));
|
||||||
const SStudio = withData(StudioClock);
|
const SStudio = withAlias(withData(StudioClock));
|
||||||
|
|
||||||
const EditorFeatureWrapper = lazy(() => import('./features/EditorFeatureWrapper'));
|
const EditorFeatureWrapper = lazy(() => import('./features/EditorFeatureWrapper'));
|
||||||
const RundownPanel = lazy(() => import('./features/rundown/RundownExport'));
|
const RundownPanel = lazy(() => import('./features/rundown/RundownExport'));
|
||||||
@@ -33,22 +32,6 @@ const MessageControl = lazy(() => import('./features/control/message/MessageCont
|
|||||||
const Info = lazy(() => import('./features/info/InfoExport'));
|
const Info = lazy(() => import('./features/info/InfoExport'));
|
||||||
|
|
||||||
export default function AppRouter() {
|
export default function AppRouter() {
|
||||||
const { data } = useAliases();
|
|
||||||
const location = useLocation();
|
|
||||||
const navigate = useNavigate();
|
|
||||||
|
|
||||||
// navigate if is alias route
|
|
||||||
useEffect(() => {
|
|
||||||
if (!data) return;
|
|
||||||
|
|
||||||
for (const d of data) {
|
|
||||||
if (`/${d.alias}` === location.pathname && d.enabled) {
|
|
||||||
navigate(`/${d.pathAndParams}`);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, [data, location, navigate]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Suspense fallback={null}>
|
<Suspense fallback={null}>
|
||||||
<Routes>
|
<Routes>
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { validateAlias } from '../aliases';
|
import { resolvePath } from 'react-router-dom';
|
||||||
|
import { validateAlias, generateURLFromAlias, getAliasRoute } from '../aliases';
|
||||||
|
|
||||||
describe('An alias fails if incorrect', () => {
|
describe('An alias fails if incorrect', () => {
|
||||||
const testsToFail = [
|
const testsToFail = [
|
||||||
@@ -23,3 +24,79 @@ describe('An alias fails if incorrect', () => {
|
|||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
describe('generateURLFromAlias and getAliasRoute function', () => {
|
||||||
|
test('generate the expected url from an alias', () => {
|
||||||
|
const testData = [
|
||||||
|
{
|
||||||
|
enabled: true,
|
||||||
|
alias: 'demopage',
|
||||||
|
pathAndParams: '/timer?user=guest',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const expected = [
|
||||||
|
{
|
||||||
|
url: '/timer?user=guest&alias=demopage',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
expect(generateURLFromAlias(testData[0])).toStrictEqual(expected[0].url);
|
||||||
|
});
|
||||||
|
test('generate the url to redirect to when the current URL is just the alias', () => {
|
||||||
|
const aliases = [
|
||||||
|
{
|
||||||
|
enabled: true,
|
||||||
|
alias: 'demopage',
|
||||||
|
pathAndParams: '/timer?user=guest',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
// let current location be the alias
|
||||||
|
const location = resolvePath(aliases[0].alias);
|
||||||
|
|
||||||
|
const expected = [
|
||||||
|
{
|
||||||
|
url: '/timer?user=guest&alias=demopage',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
expect(getAliasRoute(location, aliases, null)).toStrictEqual(expected[0].url);
|
||||||
|
});
|
||||||
|
test('generate the url to redirect to when the current URL the same url but with a change of params', () => {
|
||||||
|
const aliases = [
|
||||||
|
{
|
||||||
|
enabled: true,
|
||||||
|
alias: 'demopage',
|
||||||
|
pathAndParams: '/timer?user=guest',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
// let current location be the actual url with alias attached to it
|
||||||
|
const location = resolvePath(aliases[0].pathAndParams);
|
||||||
|
const urlSearchParams = new URLSearchParams(location.search);
|
||||||
|
urlSearchParams.append('alias', aliases[0].alias); //
|
||||||
|
|
||||||
|
// update current alias with extra param
|
||||||
|
aliases[0].pathAndParams += '&eventId=674';
|
||||||
|
const expected = [
|
||||||
|
{
|
||||||
|
url: '/timer?user=guest&eventId=674&alias=demopage',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
expect(getAliasRoute(location, aliases, urlSearchParams)).toStrictEqual(expected[0].url);
|
||||||
|
});
|
||||||
|
test('generate no url to redirect to when the current URL the same url', () => {
|
||||||
|
const aliases = [
|
||||||
|
{
|
||||||
|
enabled: true,
|
||||||
|
alias: 'demopage',
|
||||||
|
pathAndParams: '/timer?user=guest',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
// let current location be the actual url with alias attached to it
|
||||||
|
const location = resolvePath(aliases[0].pathAndParams);
|
||||||
|
const urlSearchParams = new URLSearchParams(location.search);
|
||||||
|
urlSearchParams.append('alias', aliases[0].alias); //
|
||||||
|
|
||||||
|
expect(getAliasRoute(location, aliases, urlSearchParams)).toBeNull();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
@@ -1,10 +1,13 @@
|
|||||||
|
import { Alias } from 'ontime-types';
|
||||||
|
import isEqual from 'react-fast-compare';
|
||||||
|
import { Location, resolvePath } from 'react-router-dom';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validates an alias against defined parameters
|
* Validates an alias against defined parameters
|
||||||
* @param {string} alias
|
* @param {string} alias
|
||||||
* @returns {{message: string, status: boolean}}
|
* @returns {{message: string, status: boolean}}
|
||||||
*/
|
*/
|
||||||
export const validateAlias = (alias: string) => {
|
export const validateAlias = (alias: string) => {
|
||||||
|
|
||||||
const valid = { status: true, message: 'ok' };
|
const valid = { status: true, message: 'ok' };
|
||||||
|
|
||||||
if (alias === '' || alias == null) {
|
if (alias === '' || alias == null) {
|
||||||
@@ -26,4 +29,49 @@ export const validateAlias = (alias: string) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return valid;
|
return valid;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the URL to send an alias to
|
||||||
|
* @param location
|
||||||
|
* @param data
|
||||||
|
* @param searchParams
|
||||||
|
*/
|
||||||
|
export const getAliasRoute = (location: Location, data: Alias[], searchParams: URLSearchParams) => {
|
||||||
|
const currentURL = location.pathname.substring(1);
|
||||||
|
// we need to check if the whole url here is an alias, so we can redirect
|
||||||
|
const foundAlias = data.filter((d) => d.alias === currentURL && d.enabled)[0];
|
||||||
|
if (foundAlias) {
|
||||||
|
return generateURLFromAlias(foundAlias);
|
||||||
|
}
|
||||||
|
const aliasOnPage = searchParams.get('alias');
|
||||||
|
for (const d of data) {
|
||||||
|
if (aliasOnPage) {
|
||||||
|
// if the alias fits the alias on this page, but the URL is diferent, we redirect user to the new URL
|
||||||
|
// if we have the same alias and its enabled and its not empty
|
||||||
|
if (d.alias !== '' && d.enabled && d.alias === aliasOnPage) {
|
||||||
|
const newAliasPath = resolvePath(d.pathAndParams);
|
||||||
|
const urlParams = new URLSearchParams(newAliasPath.search);
|
||||||
|
urlParams.set('alias', d.alias);
|
||||||
|
// we confirm either the url parameters does not match or the url path doesnt
|
||||||
|
if (!isEqual(urlParams, searchParams) || newAliasPath.pathname !== location.pathname) {
|
||||||
|
// we then redirect to the alias route, since the view listening to this alias has an outdated URL
|
||||||
|
return `${newAliasPath.pathname}?${urlParams}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate URL from an alias
|
||||||
|
* @param aliasData
|
||||||
|
*/
|
||||||
|
export const generateURLFromAlias = (aliasData: Alias) => {
|
||||||
|
const newAliasPath = resolvePath(aliasData.pathAndParams);
|
||||||
|
const urlParams = new URLSearchParams(newAliasPath.search);
|
||||||
|
urlParams.set('alias', aliasData.alias);
|
||||||
|
|
||||||
|
return `${newAliasPath.pathname}?${urlParams}`;
|
||||||
|
};
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
/* eslint-disable react/display-name */
|
||||||
|
import useAliases from '../common/hooks-query/useAliases';
|
||||||
|
import { getAliasRoute } from '../common/utils/aliases';
|
||||||
|
import { ComponentType, useEffect } from 'react';
|
||||||
|
import { useSearchParams, useNavigate, useLocation } from 'react-router-dom';
|
||||||
|
|
||||||
|
const withAlias = <P extends object>(Component: ComponentType<P>) => {
|
||||||
|
return (props: Partial<P>) => {
|
||||||
|
const { data } = useAliases();
|
||||||
|
const [searchParams] = useSearchParams();
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const location = useLocation();
|
||||||
|
|
||||||
|
// navigate if is alias route
|
||||||
|
useEffect(() => {
|
||||||
|
if (!data) return;
|
||||||
|
const url = getAliasRoute(location, data, searchParams);
|
||||||
|
// navigate to this route if its not empty
|
||||||
|
if (url) {
|
||||||
|
navigate(url);
|
||||||
|
}
|
||||||
|
}, [data, searchParams, navigate, location]);
|
||||||
|
|
||||||
|
return <Component {...props} />;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default withAlias;
|
||||||
@@ -131,7 +131,7 @@ export default function AliasesForm() {
|
|||||||
isDisabled={disableInputs}
|
isDisabled={disableInputs}
|
||||||
/>
|
/>
|
||||||
<TooltipActionBtn
|
<TooltipActionBtn
|
||||||
clickHandler={(event) => handleLinks(event, alias.pathAndParams)}
|
clickHandler={(event) => handleLinks(event, alias.alias)}
|
||||||
tooltip='Test alias'
|
tooltip='Test alias'
|
||||||
aria-label='Test alias'
|
aria-label='Test alias'
|
||||||
size='xs'
|
size='xs'
|
||||||
|
|||||||
Reference in New Issue
Block a user