Extract initial Shlink logic to ShlinkWebComponent

This commit is contained in:
Alejandro Celaya
2023-07-16 22:54:49 +02:00
parent d82c0dc75e
commit 682de08204
20 changed files with 197 additions and 157 deletions

View File

@@ -10,12 +10,10 @@ import classNames from 'classnames';
import type { FC } from 'react';
import type { NavLinkProps } from 'react-router-dom';
import { NavLink, useLocation } from 'react-router-dom';
import type { SelectedServer } from '../servers/data';
import { isServerWithId } from '../servers/data';
import './AsideMenu.scss';
export interface AsideMenuProps {
selectedServer: SelectedServer;
routePrefix: string;
showOnMobile?: boolean;
}
@@ -34,14 +32,12 @@ const AsideMenuItem: FC<AsideMenuItemProps> = ({ children, to, className, ...res
</NavLink>
);
export const AsideMenu: FC<AsideMenuProps> = ({ selectedServer, showOnMobile = false }) => {
const hasId = isServerWithId(selectedServer);
const serverId = hasId ? selectedServer.id : '';
export const AsideMenu: FC<AsideMenuProps> = ({ routePrefix, showOnMobile = false }) => {
const { pathname } = useLocation();
const asideClass = classNames('aside-menu', {
'aside-menu--hidden': !showOnMobile,
});
const buildPath = (suffix: string) => `/server/${serverId}${suffix}`;
const buildPath = (suffix: string) => `${routePrefix}${suffix}`;
return (
<aside className={asideClass}>

View File

@@ -1,15 +1,8 @@
import { faBars as burgerIcon } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import classNames from 'classnames';
import type { FC } from 'react';
import { useEffect } from 'react';
import { Navigate, Route, Routes, useLocation } from 'react-router-dom';
import { isReachableServer } from '../servers/data';
import { withSelectedServer } from '../servers/helpers/withSelectedServer';
import { useFeature } from '../utils/helpers/features';
import { useSwipeable, useToggle } from '../utils/helpers/hooks';
import { AsideMenu } from './AsideMenu';
import { NotFound } from './NotFound';
import type { ShlinkWebComponentType } from '../shlink-web-component';
import './MenuLayout.scss';
interface MenuLayoutProps {
@@ -18,24 +11,11 @@ interface MenuLayoutProps {
}
export const MenuLayout = (
TagsList: FC,
ShortUrlsList: FC,
CreateShortUrl: FC,
ShortUrlVisits: FC,
TagVisits: FC,
DomainVisits: FC,
OrphanVisits: FC,
NonOrphanVisits: FC,
ServerError: FC,
Overview: FC,
EditShortUrl: FC,
ManageDomains: FC,
ShlinkWebComponent: ShlinkWebComponentType,
) => withSelectedServer<MenuLayoutProps>(({ selectedServer, sidebarNotPresent, sidebarPresent }) => {
const location = useLocation();
const [sidebarVisible, toggleSidebar, showSidebar, hideSidebar] = useToggle();
const showContent = isReachableServer(selectedServer);
useEffect(() => hideSidebar(), [location]);
useEffect(() => {
showContent && sidebarPresent();
return () => sidebarNotPresent();
@@ -45,42 +25,10 @@ export const MenuLayout = (
return <ServerError />;
}
const addNonOrphanVisitsRoute = useFeature('nonOrphanVisits', selectedServer);
const addDomainVisitsRoute = useFeature('domainVisits', selectedServer);
const burgerClasses = classNames('menu-layout__burger-icon', { 'menu-layout__burger-icon--active': sidebarVisible });
const swipeableProps = useSwipeable(showSidebar, hideSidebar);
return (
<>
<FontAwesomeIcon icon={burgerIcon} className={burgerClasses} onClick={toggleSidebar} />
<div {...swipeableProps} className="menu-layout__swipeable">
<div className="menu-layout__swipeable-inner">
<AsideMenu selectedServer={selectedServer} showOnMobile={sidebarVisible} />
<div className="menu-layout__container" onClick={() => hideSidebar()}>
<div className="container-xl">
<Routes>
<Route index element={<Navigate replace to="overview" />} />
<Route path="/overview" element={<Overview />} />
<Route path="/list-short-urls/:page" element={<ShortUrlsList />} />
<Route path="/create-short-url" element={<CreateShortUrl />} />
<Route path="/short-code/:shortCode/visits/*" element={<ShortUrlVisits />} />
<Route path="/short-code/:shortCode/edit" element={<EditShortUrl />} />
<Route path="/tag/:tag/visits/*" element={<TagVisits />} />
{addDomainVisitsRoute && <Route path="/domain/:domain/visits/*" element={<DomainVisits />} />}
<Route path="/orphan-visits/*" element={<OrphanVisits />} />
{addNonOrphanVisitsRoute && <Route path="/non-orphan-visits/*" element={<NonOrphanVisits />} />}
<Route path="/manage-tags" element={<TagsList />} />
<Route path="/manage-domains" element={<ManageDomains />} />
<Route
path="*"
element={<NotFound to={`/server/${selectedServer.id}/list-short-urls/1`}>List short URLs</NotFound>}
/>
</Routes>
</div>
</div>
</div>
</div>
</>
<ShlinkWebComponent
serverVersion={selectedServer.version}
routesPrefix={`/server/${selectedServer.id}`}
/>
);
}, ServerError);

View File

@@ -1,5 +1,9 @@
import { createSlice } from '@reduxjs/toolkit';
// TODO This is only used for some components to have extra paddings/styles if existing section has a side menu
// Now that's basically the route which renders ShlinkWebComponent, so maybe there's some way to re-think this
// logic, and perhaps get rid of a reducer just for that
export interface Sidebar {
sidebarPresent: boolean;
}

View File

@@ -31,22 +31,7 @@ export const provideServices = (bottle: Bottle, connect: ConnectDecorator) => {
bottle.decorator('Home', withoutSelectedServer);
bottle.decorator('Home', connect(['servers'], ['resetSelectedServer']));
bottle.serviceFactory(
'MenuLayout',
MenuLayout,
'TagsList',
'ShortUrlsList',
'CreateShortUrl',
'ShortUrlVisits',
'TagVisits',
'DomainVisits',
'OrphanVisits',
'NonOrphanVisits',
'ServerError',
'Overview',
'EditShortUrl',
'ManageDomains',
);
bottle.serviceFactory('MenuLayout', MenuLayout, 'ServerError', 'ShlinkWebComponent');
bottle.decorator('MenuLayout', connect(['selectedServer'], ['selectServer', 'sidebarPresent', 'sidebarNotPresent']));
bottle.serviceFactory('ShlinkVersionsContainer', () => ShlinkVersionsContainer);