mirror of
https://github.com/Stremio/stremio-web.git
synced 2026-03-11 21:27:05 +00:00
121 lines
4.1 KiB
JavaScript
121 lines
4.1 KiB
JavaScript
const React = require('react');
|
|
const PropTypes = require('prop-types');
|
|
const UrlUtils = require('url');
|
|
const { RoutesContainerProvider } = require('../RoutesContainerContext');
|
|
const Route = require('./Route');
|
|
|
|
const Router = ({ homePath, viewsConfig, onPathNotMatch }) => {
|
|
const [views, setViews] = React.useState(() => {
|
|
return Array(viewsConfig.length).fill({
|
|
routeConfig: null,
|
|
queryParams: null,
|
|
urlParams: null
|
|
});
|
|
});
|
|
const routeConfigForPath = React.useCallback((path) => {
|
|
for (const viewConfig of viewsConfig) {
|
|
for (const routeConfig of viewConfig) {
|
|
const match = routeConfig.regexp.exec(path);
|
|
if (match) {
|
|
return routeConfig;
|
|
}
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}, []);
|
|
const onLocationHashChanged = React.useCallback(() => {
|
|
const { pathname, query } = UrlUtils.parse(window.location.hash.slice(1));
|
|
const routeConfig = routeConfigForPath(pathname);
|
|
if (routeConfig === null) {
|
|
if (typeof onPathNotMatch === 'function') {
|
|
onPathNotMatch();
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
const routeViewIndex = viewsConfig.findIndex((v) => v.includes(routeConfig));
|
|
const match = routeConfig.regexp.exec(pathname);
|
|
const queryParams = Array.from(new URLSearchParams(query !== null ? query : '').entries())
|
|
.reduce((result, [key, value]) => {
|
|
result[key] = value;
|
|
return result;
|
|
}, {});
|
|
const urlParams = routeConfig.keys.reduce((urlParams, key, index) => {
|
|
if (typeof match[index + 1] === 'string') {
|
|
urlParams[key.name] = match[index + 1];
|
|
} else {
|
|
urlParams[key.name] = null;
|
|
}
|
|
|
|
return urlParams;
|
|
}, {});
|
|
setViews((views) => {
|
|
return views.map((view, index) => {
|
|
if (index < routeViewIndex) {
|
|
return view;
|
|
} else if (index === routeViewIndex) {
|
|
return {
|
|
routeConfig,
|
|
queryParams,
|
|
urlParams
|
|
};
|
|
} else {
|
|
return {
|
|
routeConfig: null,
|
|
queryParams: null,
|
|
urlParams: null
|
|
};
|
|
}
|
|
});
|
|
});
|
|
}, []);
|
|
React.useEffect(() => {
|
|
if (typeof homePath === 'string') {
|
|
const { pathname, path } = UrlUtils.parse(window.location.hash.slice(1));
|
|
if (homePath !== path) {
|
|
window.location.replace(`#${homePath}`);
|
|
const routeConfig = routeConfigForPath(pathname);
|
|
if (routeConfig !== null) {
|
|
window.location = `#${path}`;
|
|
}
|
|
}
|
|
}
|
|
|
|
window.addEventListener('hashchange', onLocationHashChanged);
|
|
onLocationHashChanged();
|
|
return () => {
|
|
window.removeEventListener('hashchange', onLocationHashChanged);
|
|
};
|
|
}, []);
|
|
return (
|
|
<RoutesContainerProvider>
|
|
{
|
|
views
|
|
.filter(({ routeConfig }) => routeConfig !== null)
|
|
.map(({ routeConfig, queryParams, urlParams }) => (
|
|
<Route key={routeConfig.component.name}>
|
|
{React.createElement(routeConfig.component, { queryParams, urlParams })}
|
|
</Route>
|
|
))
|
|
}
|
|
</RoutesContainerProvider>
|
|
);
|
|
};
|
|
|
|
Router.propTypes = {
|
|
homePath: PropTypes.string,
|
|
onPathNotMatch: PropTypes.func,
|
|
viewsConfig: PropTypes.arrayOf(PropTypes.arrayOf(PropTypes.exact({
|
|
regexp: PropTypes.shape({
|
|
exec: PropTypes.func.isRequired
|
|
}).isRequired,
|
|
keys: PropTypes.arrayOf(PropTypes.shape({
|
|
name: PropTypes.string.isRequired
|
|
})).isRequired,
|
|
component: PropTypes.elementType.isRequired
|
|
}))).isRequired
|
|
};
|
|
|
|
module.exports = Router;
|